X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d85423250a7e8f4c84d03fcdb1804c3539b1f80c..eea4d01c65f9b29baa1193db762b4c6b8144af24:/src/propgrid/propgrid.cpp?ds=sidebyside diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 234f372485..5af8805a19 100644 --- a/src/propgrid/propgrid.cpp +++ b/src/propgrid/propgrid.cpp @@ -4,9 +4,9 @@ // Author: Jaakko Salli // Modified by: // Created: 2004-09-25 -// RCS-ID: $Id: +// RCS-ID: $Id$ // Copyright: (c) Jaakko Salli -// Licence: wxWindows license +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx/wx.h". @@ -52,23 +52,18 @@ // This define is necessary to prevent macro clearing #define __wxPG_SOURCE_FILE__ -#include -#include +#include "wx/propgrid/propgrid.h" +#include "wx/propgrid/editors.h" #if wxPG_USE_RENDERER_NATIVE - #include + #include "wx/renderer.h" #endif -#include +#include "wx/odcombo.h" #include "wx/timer.h" #include "wx/dcbuffer.h" -#include -#include - -#ifdef __WXMSW__ - #include -#endif +#include "wx/scopeguard.h" // Two pics for the expand / collapse buttons. // Files are not supplied with this project (since it is @@ -92,16 +87,16 @@ //#define wxPG_TEXT_INDENT 4 // For the wxComboControl -#define wxPG_ALLOW_CLIPPING 1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored +//#define wxPG_ALLOW_CLIPPING 1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored #define wxPG_GUTTER_DIV 3 // gutter is max(iconwidth/gutter_div,gutter_min) #define wxPG_GUTTER_MIN 3 // gutter before and after image of [+] or [-] #define wxPG_YSPACING_MIN 1 #define wxPG_DEFAULT_VSPACING 2 // This matches .NET propertygrid's value, // but causes normal combobox to spill out under MSW -#define wxPG_OPTIMAL_WIDTH 200 // Arbitrary +//#define wxPG_OPTIMAL_WIDTH 200 // Arbitrary -#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform +//#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform // Must be larger than largest control border // width * 2. @@ -111,7 +106,7 @@ //#define wxPG_NAT_CHOICE_BORDER_ANY 0 -#define wxPG_HIDER_BUTTON_HEIGHT 25 +//#define wxPG_HIDER_BUTTON_HEIGHT 25 #define wxPG_PIXELS_PER_UNIT m_lineHeight @@ -119,7 +114,12 @@ #define m_iconHeight m_iconWidth #endif -#define wxPG_TOOLTIP_DELAY 1000 +//#define wxPG_TOOLTIP_DELAY 1000 + +// This is the number of pixels the expander button inside +// property cells (i.e. not in the grey margin area are +// adjusted. +#define IN_CELL_EXPANDER_BUTTON_X_ADJUST 2 // ----------------------------------------------------------------------- @@ -134,13 +134,13 @@ void wxPropertyGrid::AutoGetTranslation ( bool ) { } // ----------------------------------------------------------------------- -const wxChar *wxPropertyGridNameStr = wxT("wxPropertyGrid"); +const char wxPropertyGridNameStr[] = "wxPropertyGrid"; // ----------------------------------------------------------------------- // Statics in one class for easy destruction. // ----------------------------------------------------------------------- -#include +#include "wx/module.h" class wxPGGlobalVarsClassManager : public wxModule { @@ -148,13 +148,22 @@ class wxPGGlobalVarsClassManager : public wxModule public: wxPGGlobalVarsClassManager() {} virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; } - virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; } + virtual void OnExit() { wxDELETE(wxPGGlobalVars); } }; IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule) -wxPGGlobalVarsClass* wxPGGlobalVars = (wxPGGlobalVarsClass*) NULL; +// When wxPG is loaded dynamically after the application is already running +// then the built-in module system won't pick this one up. Add it manually. +void wxPGInitResourceModule() +{ + wxModule* module = new wxPGGlobalVarsClassManager; + module->Init(); + wxModule::RegisterModule(module); +} + +wxPGGlobalVarsClass* wxPGGlobalVars = NULL; wxPGGlobalVarsClass::wxPGGlobalVarsClass() @@ -164,7 +173,7 @@ wxPGGlobalVarsClass::wxPGGlobalVarsClass() m_boolChoices.Add(_("False")); m_boolChoices.Add(_("True")); - m_fontFamilyChoices = (wxPGChoices*) NULL; + m_fontFamilyChoices = NULL; m_defaultRenderer = new wxPGDefaultRenderer(); @@ -176,7 +185,7 @@ wxPGGlobalVarsClass::wxPGGlobalVarsClass() wxVariant v; - // Prepare some shared variants + // Prepare some shared variants m_vEmptyString = wxString(); m_vZero = (long) 0; m_vMinusOne = (long) -1; @@ -188,14 +197,16 @@ wxPGGlobalVarsClass::wxPGGlobalVarsClass() m_strlong = wxS("long"); m_strbool = wxS("bool"); m_strlist = wxS("list"); + m_strDefaultValue = wxS("DefaultValue"); m_strMin = wxS("Min"); m_strMax = wxS("Max"); m_strUnits = wxS("Units"); + m_strHint = wxS("Hint"); +#if wxPG_COMPATIBILITY_1_4 m_strInlineHelp = wxS("InlineHelp"); +#endif -#ifdef __WXDEBUG__ m_warnings = 0; -#endif } @@ -224,6 +235,10 @@ wxPGGlobalVarsClass::~wxPGGlobalVarsClass() delete ((wxPGEditor*)vt_it->second); } + // Make sure the global pointers have been reset + wxASSERT(wxPG_EDITOR(TextCtrl) == NULL); + wxASSERT(wxPG_EDITOR(ChoiceAndButton) == NULL); + delete wxPGProperty::sm_wxPG_LABEL; } @@ -231,248 +246,14 @@ void wxPropertyGridInitGlobalsIfNeeded() { } -// ----------------------------------------------------------------------- -// wxPGBrush -// ----------------------------------------------------------------------- - -// -// This class is a wxBrush derivative used in the background colour -// brush cache. It adds wxPG-type colour-in-long to the class. -// JMS: Yes I know wxBrush doesn't actually hold the value (refcounted -// object does), but this is simpler implementation and equally -// effective. -// - -class wxPGBrush : public wxBrush -{ -public: - wxPGBrush( const wxColour& colour ); - wxPGBrush(); - virtual ~wxPGBrush() { } - void SetColour2( const wxColour& colour ); - inline long GetColourAsLong() const { return m_colAsLong; } -private: - long m_colAsLong; -}; - - -void wxPGBrush::SetColour2( const wxColour& colour ) -{ - wxBrush::SetColour(colour); - m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue()); -} - - -wxPGBrush::wxPGBrush() : wxBrush() -{ - m_colAsLong = 0; -} - - -wxPGBrush::wxPGBrush( const wxColour& colour ) : wxBrush(colour) -{ - m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue()); -} - - -// ----------------------------------------------------------------------- -// wxPGColour -// ----------------------------------------------------------------------- - -// -// Same as wxPGBrush, but for wxColour instead. -// - -class wxPGColour : public wxColour -{ -public: - wxPGColour( const wxColour& colour ); - wxPGColour(); - virtual ~wxPGColour() { } - void SetColour2( const wxColour& colour ); - inline long GetColourAsLong() const { return m_colAsLong; } -private: - long m_colAsLong; -}; - - -void wxPGColour::SetColour2( const wxColour& colour ) -{ - *this = colour; - m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue()); -} - - -wxPGColour::wxPGColour() : wxColour() -{ - m_colAsLong = 0; -} - - -wxPGColour::wxPGColour( const wxColour& colour ) : wxColour(colour) -{ - m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue()); -} - - -// ----------------------------------------------------------------------- -// wxPGTLWHandler -// Intercepts Close-events sent to wxPropertyGrid's top-level parent, -// and tries to commit property value. -// ----------------------------------------------------------------------- - -class wxPGTLWHandler : public wxEvtHandler -{ -public: - - wxPGTLWHandler( wxPropertyGrid* pg ) - : wxEvtHandler() - { - m_pg = pg; - } - -protected: - - void OnClose( wxCloseEvent& event ) - { - // ClearSelection forces value validation/commit. - if ( event.CanVeto() && !m_pg->ClearSelection() ) - { - event.Veto(); - return; - } - - event.Skip(); - } - -private: - wxPropertyGrid* m_pg; - - DECLARE_EVENT_TABLE() -}; - -BEGIN_EVENT_TABLE(wxPGTLWHandler, wxEvtHandler) - EVT_CLOSE(wxPGTLWHandler::OnClose) -END_EVENT_TABLE() - -// ----------------------------------------------------------------------- -// wxPGCanvas -// ----------------------------------------------------------------------- - -// -// wxPGCanvas acts as a graphics sub-window of the -// wxScrolledWindow that wxPropertyGrid is. -// -class wxPGCanvas : public wxPanel -{ -public: - wxPGCanvas() : wxPanel() - { - } - virtual ~wxPGCanvas() { } - -protected: - void OnMouseMove( wxMouseEvent &event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnMouseMove( event ); - } - - void OnMouseClick( wxMouseEvent &event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnMouseClick( event ); - } - - void OnMouseUp( wxMouseEvent &event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnMouseUp( event ); - } - - void OnMouseRightClick( wxMouseEvent &event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnMouseRightClick( event ); - } - - void OnMouseDoubleClick( wxMouseEvent &event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnMouseDoubleClick( event ); - } - - void OnKey( wxKeyEvent& event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnKey( event ); - } - - void OnKeyUp( wxKeyEvent& event ) - { - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - pg->OnKeyUp( event ); - } - - void OnPaint( wxPaintEvent& event ); - - // Always be focussable, even with child windows - virtual void SetCanFocus(bool WXUNUSED(canFocus)) - { wxPanel::SetCanFocus(true); } - - -private: - DECLARE_EVENT_TABLE() - DECLARE_ABSTRACT_CLASS(wxPGCanvas) -}; - - -IMPLEMENT_ABSTRACT_CLASS(wxPGCanvas,wxPanel) - -BEGIN_EVENT_TABLE(wxPGCanvas, wxPanel) - EVT_MOTION(wxPGCanvas::OnMouseMove) - EVT_PAINT(wxPGCanvas::OnPaint) - EVT_LEFT_DOWN(wxPGCanvas::OnMouseClick) - EVT_LEFT_UP(wxPGCanvas::OnMouseUp) - EVT_RIGHT_UP(wxPGCanvas::OnMouseRightClick) - EVT_LEFT_DCLICK(wxPGCanvas::OnMouseDoubleClick) - EVT_KEY_DOWN(wxPGCanvas::OnKey) - EVT_KEY_UP(wxPGCanvas::OnKeyUp) - EVT_CHAR(wxPGCanvas::OnKey) -END_EVENT_TABLE() - - -void wxPGCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) ) -{ - wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid); - wxASSERT( pg->IsKindOf(CLASSINFO(wxPropertyGrid)) ); - - wxPaintDC dc(this); - - // Don't paint after destruction has begun - if ( !(pg->GetInternalFlags() & wxPG_FL_INITIALIZED) ) - return; - - // Update everything inside the box - wxRect r = GetUpdateRegion().GetBox(); - - // Repaint this rectangle - pg->DrawItems( dc, r.y, r.y + r.height, &r ); - - // We assume that the size set when grid is shown - // is what is desired. - pg->SetInternalFlag(wxPG_FL_GOOD_SIZE_SET); -} - // ----------------------------------------------------------------------- // wxPropertyGrid // ----------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxScrolledWindow) +IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxControl) -BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow) +BEGIN_EVENT_TABLE(wxPropertyGrid, wxControl) EVT_IDLE(wxPropertyGrid::OnIdle) - EVT_MOTION(wxPropertyGrid::OnMouseMoveBottom) EVT_PAINT(wxPropertyGrid::OnPaint) EVT_SIZE(wxPropertyGrid::OnResize) EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry) @@ -483,13 +264,18 @@ BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow) EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent) EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent) EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged) + EVT_MOTION(wxPropertyGrid::OnMouseMove) + EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick) + EVT_LEFT_UP(wxPropertyGrid::OnMouseUp) + EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick) + EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick) + EVT_KEY_DOWN(wxPropertyGrid::OnKey) END_EVENT_TABLE() - // ----------------------------------------------------------------------- wxPropertyGrid::wxPropertyGrid() - : wxScrolledWindow() + : wxControl(), wxScrollHelper(this) { Init1(); } @@ -501,8 +287,8 @@ wxPropertyGrid::wxPropertyGrid( wxWindow *parent, const wxPoint& pos, const wxSize& size, long style, - const wxChar* name ) - : wxScrolledWindow() + const wxString& name ) + : wxControl(), wxScrollHelper(this) { Init1(); Create(parent,id,pos,size,style,name); @@ -515,28 +301,24 @@ bool wxPropertyGrid::Create( wxWindow *parent, const wxPoint& pos, const wxSize& size, long style, - const wxChar* name ) + const wxString& name ) { - if ( !(style&wxBORDER_MASK) ) - style |= wxSIMPLE_BORDER; + if (!(style&wxBORDER_MASK)) + { + style |= wxBORDER_THEME; + } style |= wxVSCROLL; -#ifdef __WXMSW__ - // This prevents crash under Win2K, but still - // enables keyboard navigation - if ( style & wxTAB_TRAVERSAL ) - { - style &= ~(wxTAB_TRAVERSAL); - style |= wxWANTS_CHARS; - } -#else - if ( style & wxTAB_TRAVERSAL ) - style |= wxWANTS_CHARS; -#endif + // Filter out wxTAB_TRAVERSAL - we will handle TABs manually + style &= ~(wxTAB_TRAVERSAL); + style |= wxWANTS_CHARS; - wxScrolledWindow::Create(parent,id,pos,size,style,name); + wxControl::Create(parent, id, pos, size, + style | wxScrolledWindowStyle, + wxDefaultValidator, + name); Init2(); @@ -554,23 +336,31 @@ void wxPropertyGrid::Init1() if ( wxPGGlobalVars->m_mapEditorClasses.empty() ) wxPropertyGrid::RegisterDefaultEditors(); + m_validatingEditor = 0; m_iFlags = 0; - m_pState = (wxPropertyGridPageState*) NULL; - m_wndEditor = m_wndEditor2 = (wxWindow*) NULL; - m_selected = (wxPGProperty*) NULL; - m_selColumn = -1; - m_propHover = (wxPGProperty*) NULL; + m_pState = NULL; + m_wndEditor = m_wndEditor2 = NULL; + m_selColumn = 1; + m_colHover = 1; + m_propHover = NULL; + m_labelEditor = NULL; + m_labelEditorProperty = NULL; m_eventObject = this; - m_curFocused = (wxWindow*) NULL; - m_tlwHandler = NULL; - m_inDoPropertyChanged = 0; - m_inCommitChangesFromEditor = 0; - m_inDoSelectProperty = 0; + m_curFocused = NULL; + m_processedEvent = NULL; + m_sortFunction = NULL; + m_inDoPropertyChanged = false; + m_inCommitChangesFromEditor = false; + m_inDoSelectProperty = false; + m_inOnValidationFailure = false; m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT; m_dragStatus = 0; m_mouseSide = 16; m_editorFocused = 0; + // Set up default unspecified value 'colour' + m_unspecifiedAppearance.SetFgCol(*wxLIGHT_GREY); + // Set default keys AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT ); AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN ); @@ -579,28 +369,19 @@ void wxPropertyGrid::Init1() AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY, WXK_RIGHT); AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY, WXK_LEFT); AddActionTrigger( wxPG_ACTION_CANCEL_EDIT, WXK_ESCAPE ); - AddActionTrigger( wxPG_ACTION_CUT, 'X', wxMOD_CONTROL ); - AddActionTrigger( wxPG_ACTION_CUT, WXK_DELETE, wxMOD_SHIFT ); - AddActionTrigger( wxPG_ACTION_COPY, 'C', wxMOD_CONTROL); - AddActionTrigger( wxPG_ACTION_COPY, WXK_INSERT, wxMOD_CONTROL ); - AddActionTrigger( wxPG_ACTION_PASTE, 'V', wxMOD_CONTROL ); - AddActionTrigger( wxPG_ACTION_PASTE, WXK_INSERT, wxMOD_SHIFT ); AddActionTrigger( wxPG_ACTION_PRESS_BUTTON, WXK_DOWN, wxMOD_ALT ); + AddActionTrigger( wxPG_ACTION_PRESS_BUTTON, WXK_F4 ); m_coloursCustomized = 0; m_frozen = 0; - m_canvas = NULL; - -#if wxPG_DOUBLE_BUFFER - m_doubleBuffer = (wxBitmap*) NULL; -#endif + m_doubleBuffer = NULL; #ifndef wxPG_ICON_WIDTH - m_expandbmp = NULL; - m_collbmp = NULL; - m_iconWidth = 11; - m_iconHeight = 11; + m_expandbmp = NULL; + m_collbmp = NULL; + m_iconWidth = 11; + m_iconHeight = 11; #else m_iconWidth = wxPG_ICON_WIDTH; #endif @@ -632,7 +413,7 @@ void wxPropertyGrid::Init2() #ifdef __WXMAC__ // Smaller controls on Mac SetWindowVariant(wxWINDOW_VARIANT_SMALL); -#endif +#endif // Now create state, if one didn't exist already // (wxPropertyGridManager might have created it for us). @@ -644,7 +425,7 @@ void wxPropertyGrid::Init2() } if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) ) - m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER; + m_pState->m_dontCenterSplitter = true; if ( m_windowStyle & wxPG_HIDE_CATEGORIES ) { @@ -657,59 +438,43 @@ void wxPropertyGrid::Init2() #ifndef wxPG_ICON_WIDTH // create two bitmap nodes for drawing - m_expandbmp = new wxBitmap(expand_xpm); - m_collbmp = new wxBitmap(collapse_xpm); + m_expandbmp = new wxBitmap(expand_xpm); + m_collbmp = new wxBitmap(collapse_xpm); - // calculate average font height for bitmap centering + // calculate average font height for bitmap centering - m_iconWidth = m_expandbmp->GetWidth(); - m_iconHeight = m_expandbmp->GetHeight(); + m_iconWidth = m_expandbmp->GetWidth(); + m_iconHeight = m_expandbmp->GetHeight(); #endif m_curcursor = wxCURSOR_ARROW; m_cursorSizeWE = new wxCursor( wxCURSOR_SIZEWE ); - // adjust bitmap icon y position so they are centered + // adjust bitmap icon y position so they are centered m_vspacing = wxPG_DEFAULT_VSPACING; - if ( !m_font.Ok() ) - { - wxFont useFont = wxScrolledWindow::GetFont(); - wxScrolledWindow::SetOwnFont( useFont ); - } - else - // This should be otherwise called by SetOwnFont - CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING ); - - // Add base brush item - m_arrBgBrushes.Add((void*)new wxPGBrush()); + CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING ); - // Add base colour items - m_arrFgCols.Add((void*)new wxPGColour()); - m_arrFgCols.Add((void*)new wxPGColour()); + // Allocate cell datas + m_propertyDefaultCell.SetEmptyData(); + m_categoryDefaultCell.SetEmptyData(); RegainColours(); // This helps with flicker SetBackgroundStyle( wxBG_STYLE_CUSTOM ); - // Hook the TLW - wxPGTLWHandler* handler = new wxPGTLWHandler(this); - m_tlp = ::wxGetTopLevelParent(this); - m_tlwHandler = handler; - m_tlp->PushEventHandler(handler); + // Hook the top-level parent + m_tlp = NULL; + m_tlpClosed = NULL; + m_tlpClosedTime = 0; - // set virtual size to this window size + // set virtual size to this window size wxSize wndsize = GetSize(); - SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth()); + SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth()); m_timeCreated = ::wxGetLocalTimeMillis(); - m_canvas = new wxPGCanvas(); - m_canvas->Create(this, 1, wxPoint(0, 0), GetClientSize(), - (GetWindowStyle() & wxTAB_TRAVERSAL) | wxWANTS_CHARS | wxCLIP_CHILDREN); - m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM ); - m_iFlags |= wxPG_FL_INITIALIZED; m_ncWidth = wndsize.GetWidth(); @@ -726,30 +491,59 @@ wxPropertyGrid::~wxPropertyGrid() { size_t i; - DoSelectProperty(NULL); +#if wxUSE_THREADS + wxCriticalSectionLocker(wxPGGlobalVars->m_critSect); +#endif + + // + // Remove grid and property pointers from live wxPropertyGridEvents. + for ( i=0; iSetPropertyGrid(NULL); + evt->SetProperty(NULL); + } + m_liveEvents.clear(); + + if ( m_processedEvent ) + { + // All right... we are being deleted while wxPropertyGrid event + // is being sent. Make sure that event propagates as little + // as possible (although usually this is not enough to prevent + // a crash). + m_processedEvent->Skip(false); + m_processedEvent->StopPropagation(); + + // Let's use wxMessageBox to make the message appear more + // reliably (and *before* the crash can happen). + ::wxMessageBox("wxPropertyGrid was being destroyed in an event " + "generated by it. This usually leads to a crash " + "so it is recommended to destroy the control " + "at idle time instead."); + } + + 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); if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) - m_canvas->ReleaseMouse(); + ReleaseMouse(); - wxPGTLWHandler* handler = (wxPGTLWHandler*) m_tlwHandler; - m_tlp->RemoveEventHandler(handler); - delete handler; + // Call with NULL to disconnect event handling + if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING ) + { + OnTLPChanging(NULL); -#ifdef __WXDEBUG__ - if ( IsEditorsValueModified() ) - ::wxMessageBox(wxS("Most recent change in property editor was lost!!!\n\n(if you don't want this to happen, close your frames and dialogs using Close(false).)"), - wxS("wxPropertyGrid Debug Warning") ); -#endif + wxASSERT_MSG( !IsEditorsValueModified(), + wxS("Most recent change in property editor was ") + wxS("lost!!! (if you don't want this to happen, ") + wxS("close your frames and dialogs using ") + wxS("Close(false).)") ); + } -#if wxPG_DOUBLE_BUFFER if ( m_doubleBuffer ) delete m_doubleBuffer; -#endif - - //m_selected = (wxPGProperty*) NULL; if ( m_iFlags & wxPG_FL_CREATEDSTATE ) delete m_pState; @@ -757,26 +551,16 @@ wxPropertyGrid::~wxPropertyGrid() delete m_cursorSizeWE; #ifndef wxPG_ICON_WIDTH - delete m_expandbmp; - delete m_collbmp; + delete m_expandbmp; + delete m_collbmp; #endif - // Delete cached text colours. - for ( i=0; iReleaseMouse(); + ReleaseMouse(); - return wxScrolledWindow::Destroy(); + return wxControl::Destroy(); } // ----------------------------------------------------------------------- @@ -846,12 +630,12 @@ void wxPropertyGrid::SetWindowStyleFlag( long style ) // // Tooltips disabled // - m_canvas->SetToolTip( (wxToolTip*) NULL ); + SetToolTip( NULL ); } #endif } - wxScrolledWindow::SetWindowStyleFlag ( style ); + wxControl::SetWindowStyleFlag ( style ); if ( m_iFlags & wxPG_FL_INITIALIZED ) { @@ -869,7 +653,7 @@ void wxPropertyGrid::Freeze() { if ( !m_frozen ) { - wxScrolledWindow::Freeze(); + wxControl::Freeze(); } m_frozen++; } @@ -882,108 +666,604 @@ void wxPropertyGrid::Thaw() if ( !m_frozen ) { - wxScrolledWindow::Thaw(); + wxControl::Thaw(); RecalculateVirtualSize(); #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT - m_canvas->Refresh(); + Refresh(); #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 | wxPG_SEL_NONVISIBLE); } } // ----------------------------------------------------------------------- -void wxPropertyGrid::SetExtraStyle( long exStyle ) +bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags ) { - if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) - { -#if defined(__WXMSW__) - - /* - // Don't use WS_EX_COMPOSITED just now. - HWND hWnd; + wxCHECK( prop, false ); - if ( m_iFlags & wxPG_FL_IN_MANAGER ) - hWnd = (HWND)GetParent()->GetHWND(); - else - hWnd = (HWND)GetHWND(); + if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) ) + return DoSelectProperty(prop, selFlags); - ::SetWindowLong( hWnd, GWL_EXSTYLE, - ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED ); - */ + wxArrayPGProperty& selection = m_pState->m_selection; -//#elif defined(__WXGTK20__) -#endif - // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window - // truly was double-buffered. - if ( !this->IsDoubleBuffered() ) - { - exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING); - } - else - { - #if wxPG_DOUBLE_BUFFER - delete m_doubleBuffer; - m_doubleBuffer = NULL; - #endif - } + 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; - wxScrolledWindow::SetExtraStyle( exStyle ); + selection.push_back(prop); - if ( exStyle & wxPG_EX_INIT_NOCAT ) - m_pState->InitNonCatMode(); + if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) + { + SendEvent( wxEVT_PG_SELECTED, prop, NULL ); + } - if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS ) - m_windowStyle |= wxPG_TOOLTIPS; + DrawItem(prop); + } - // Set global style - wxPGGlobalVars->m_extraStyle = exStyle; + return true; } // ----------------------------------------------------------------------- -// returns the best acceptable minimal size -wxSize wxPropertyGrid::DoGetBestSize() const +bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags ) { - int hei = 15; - if ( m_lineHeight > hei ) - hei = m_lineHeight; - wxSize sz = wxSize( 60, hei+40 ); + wxCHECK( prop, false ); + bool res; - CacheBestSize(sz); - return sz; + wxArrayPGProperty& selection = m_pState->m_selection; + if ( selection.size() <= 1 ) + { + res = DoSelectProperty(NULL, selFlags); + } + else + { + m_pState->DoRemoveFromSelection(prop); + DrawItem(prop); + res = true; + } + + return res; } -// ----------------------------------------------------------------------- -// wxPropertyGrid Font and Colour Methods // ----------------------------------------------------------------------- -void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing ) +bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty* prop, + unsigned int colIndex, + unsigned int selFlags ) { - int x = 0, y = 0; + // + // 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 ); - m_captionFont = wxScrolledWindow::GetFont(); + bool res; - GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont); - m_subgroup_extramargin = x + (x/2); - m_fontHeight = y; + if ( colIndex == 1 ) + { + res = DoSelectProperty(prop, selFlags); + } + else + { + // send event + DoClearSelection(false, wxPG_SEL_NO_REFRESH); -#if wxPG_USE_RENDERER_NATIVE - m_iconWidth = wxPG_ICON_WIDTH; -#elif wxPG_ICON_WIDTH - // scale icon - m_iconWidth = (m_fontHeight * wxPG_ICON_WIDTH) / 13; - if ( m_iconWidth < 5 ) m_iconWidth = 5; - else if ( !(m_iconWidth & 0x01) ) m_iconWidth++; // must be odd + if ( m_pState->m_editableColumns.Index(colIndex) == wxNOT_FOUND ) + { + res = DoAddToSelection(prop, selFlags); + } + else + { + res = DoAddToSelection(prop, selFlags|wxPG_SEL_NO_REFRESH); -#endif + DoBeginLabelEdit(colIndex, selFlags); + } + } - m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV; - if ( m_gutterWidth < wxPG_GUTTER_MIN ) + //m_colSelBack = prevColSelBack; + return res; +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop, + unsigned int colIndex, + wxMouseEvent* mouseEvent, + int selFlags ) +{ + const wxArrayPGProperty& selection = GetSelectedProperties(); + bool alreadySelected = m_pState->DoIsPropertySelected(prop); + bool res = true; + + // Set to 2 if also add all items in between + int addToExistingSelection = 0; + + 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 DoSelectAndEdit(prop, colIndex, selFlags); + return true; + } + else + { + if ( mouseEvent->ControlDown() ) + { + addToExistingSelection = 1; + } + else if ( mouseEvent->ShiftDown() ) + { + if ( selection.size() > 0 && !prop->IsCategory() ) + addToExistingSelection = 2; + else + addToExistingSelection = 1; + } + } + } + } + + if ( addToExistingSelection == 1 ) + { + // Add/remove one + if ( !alreadySelected ) + { + res = DoAddToSelection(prop, selFlags); + } + else if ( GetSelectedProperties().size() > 1 ) + { + res = DoRemoveFromSelection(prop, selFlags); + } + } + else if ( addToExistingSelection == 2 ) + { + // Add this, and all in between + + // Find top selected property + wxPGProperty* topSelProp = selection[0]; + int topSelPropY = topSelProp->GetY(); + for ( unsigned int i=1; iGetY(); + if ( y < topSelPropY ) + { + topSelProp = p; + topSelPropY = y; + } + } + + wxPGProperty* startFrom; + wxPGProperty* stopAt; + + if ( prop->GetY() <= topSelPropY ) + { + // Property is above selection (or same) + startFrom = prop; + stopAt = topSelProp; + } + else + { + // Property is below selection + startFrom = topSelProp; + stopAt = prop; + } + + // Iterate through properties in-between, and select them + wxPropertyGridIterator it; + + for ( it = GetIterator(wxPG_ITERATE_VISIBLE, startFrom); + !it.AtEnd(); + it++ ) + { + wxPGProperty* p = *it; + + if ( !p->IsCategory() && + !m_pState->DoIsPropertySelected(p) ) + { + DoAddToSelection(p, selFlags); + } + + if ( p == stopAt ) + break; + } + } + else + { + res = DoSelectAndEdit(prop, colIndex, 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(); +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::MakeColumnEditable( unsigned int column, + bool editable ) +{ + wxASSERT( column != 1 ); + + wxArrayInt& cols = m_pState->m_editableColumns; + + if ( editable ) + { + cols.push_back(column); + } + else + { + for ( int i = cols.size() - 1; i > 0; i-- ) + { + if ( cols[i] == (int)column ) + cols.erase( cols.begin() + i ); + } + } +} + +// ----------------------------------------------------------------------- + +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 + { + HandleKeyEvent(event, true); + } +} + +// ----------------------------------------------------------------------- + +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; + int wasFocused = m_iFlags & wxPG_FL_FOCUSED; + + DestroyEditorWnd(m_labelEditor); + + m_labelEditor = NULL; + m_labelEditorProperty = NULL; + + // Fix focus (needed at least on wxGTK) + if ( wasFocused ) + SetFocusOnCanvas(); + + DrawItem(prop); +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::SetExtraStyle( long exStyle ) +{ + if ( exStyle & wxPG_EX_ENABLE_TLP_TRACKING ) + OnTLPChanging(::wxGetTopLevelParent(this)); + else + OnTLPChanging(NULL); + + if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) + { +#if defined(__WXMSW__) + + /* + // Don't use WS_EX_COMPOSITED just now. + HWND hWnd; + + if ( m_iFlags & wxPG_FL_IN_MANAGER ) + hWnd = (HWND)GetParent()->GetHWND(); + else + hWnd = (HWND)GetHWND(); + + ::SetWindowLong( hWnd, GWL_EXSTYLE, + ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED ); + */ + +//#elif defined(__WXGTK20__) +#endif + // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window + // truly was double-buffered. + if ( !this->IsDoubleBuffered() ) + { + exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING); + } + else + { + wxDELETE(m_doubleBuffer); + } + } + + wxControl::SetExtraStyle( exStyle ); + + if ( exStyle & wxPG_EX_INIT_NOCAT ) + m_pState->InitNonCatMode(); + + if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS ) + m_windowStyle |= wxPG_TOOLTIPS; + + // Set global style + wxPGGlobalVars->m_extraStyle = exStyle; +} + +// ----------------------------------------------------------------------- + +// returns the best acceptable minimal size +wxSize wxPropertyGrid::DoGetBestSize() const +{ + int lineHeight = wxMax(15, m_lineHeight); + + // don't make the grid too tall (limit height to 10 items) but don't + // make it too small neither + int numLines = wxMin + ( + wxMax(m_pState->m_properties->GetChildCount(), 3), + 10 + ); + + wxClientDC dc(const_cast(this)); + int width = m_marginWidth; + for ( unsigned int i = 0; i < m_pState->m_colWidths.size(); i++ ) + { + width += m_pState->GetColumnFitWidth(dc, m_pState->DoGetRoot(), i, true); + } + + const wxSize sz = wxSize(width, lineHeight*numLines + 40); + + CacheBestSize(sz); + return sz; +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP ) +{ + if ( newTLP == m_tlp ) + return; + + wxLongLong currentTime = ::wxGetLocalTimeMillis(); + + // + // Parent changed so let's redetermine and re-hook the + // correct top-level window. + if ( m_tlp ) + { + m_tlp->Disconnect( wxEVT_CLOSE_WINDOW, + wxCloseEventHandler(wxPropertyGrid::OnTLPClose), + NULL, this ); + m_tlpClosed = m_tlp; + m_tlpClosedTime = currentTime; + } + + if ( newTLP ) + { + // Only accept new tlp if same one was not just dismissed. + if ( newTLP != m_tlpClosed || + m_tlpClosedTime+250 < currentTime ) + { + newTLP->Connect( wxEVT_CLOSE_WINDOW, + wxCloseEventHandler(wxPropertyGrid::OnTLPClose), + NULL, this ); + m_tlpClosed = NULL; + } + else + { + newTLP = NULL; + } + } + + m_tlp = newTLP; +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::OnTLPClose( wxCloseEvent& event ) +{ + // ClearSelection forces value validation/commit. + if ( event.CanVeto() && !DoClearSelection() ) + { + event.Veto(); + return; + } + + // Ok, it can close, set tlp pointer to NULL. Some other event + // handler can of course veto the close, but our OnIdle() should + // then be able to regain the tlp pointer. + OnTLPChanging(NULL); + + event.Skip(); +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::Reparent( wxWindowBase *newParent ) +{ + OnTLPChanging((wxWindow*)newParent); + + bool res = wxControl::Reparent(newParent); + + return res; +} + +// ----------------------------------------------------------------------- +// wxPropertyGrid Font and Colour Methods +// ----------------------------------------------------------------------- + +void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing ) +{ + int x = 0, y = 0; + + m_captionFont = wxControl::GetFont(); + + GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont); + m_subgroup_extramargin = x + (x/2); + m_fontHeight = y; + +#if wxPG_USE_RENDERER_NATIVE + m_iconWidth = wxPG_ICON_WIDTH; +#elif wxPG_ICON_WIDTH + // scale icon + m_iconWidth = (m_fontHeight * wxPG_ICON_WIDTH) / 13; + if ( m_iconWidth < 5 ) m_iconWidth = 5; + else if ( !(m_iconWidth & 0x01) ) m_iconWidth++; // must be odd + +#endif + + m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV; + if ( m_gutterWidth < wxPG_GUTTER_MIN ) m_gutterWidth = wxPG_GUTTER_MIN; int vdiv = 6; @@ -999,7 +1279,7 @@ void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing ) m_marginWidth = m_gutterWidth*2 + m_iconWidth; m_captionFont.SetWeight(wxBOLD); - GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont); + GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont); m_lineHeight = m_fontHeight+(2*m_spacingy)+1; @@ -1078,8 +1358,6 @@ static int wxPGGetColAvg( const wxColour& col ) void wxPropertyGrid::RegainColours() { - wxColour def_bgcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); - if ( !(m_coloursCustomized & 0x0002) ) { wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ); @@ -1094,6 +1372,7 @@ void wxPropertyGrid::RegainColours() m_colCapBack = wxPGAdjustColour(col,-colDec); else m_colCapBack = col; + m_categoryDefaultCell.GetData()->SetBgCol(m_colCapBack); } if ( !(m_coloursCustomized & 0x0001) ) @@ -1108,27 +1387,25 @@ void wxPropertyGrid::RegainColours() #endif wxColour capForeCol = wxPGAdjustColour(m_colCapBack,colDec,5000,5000,true); m_colCapFore = capForeCol; - - // Set the cached colour as well. - ((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(capForeCol); + m_categoryDefaultCell.GetData()->SetFgCol(capForeCol); } if ( !(m_coloursCustomized & 0x0008) ) { wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); m_colPropBack = bgCol; - - // Set the cached brush as well. - ((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(bgCol); + m_propertyDefaultCell.GetData()->SetBgCol(bgCol); + if ( !m_unspecifiedAppearance.GetBgCol().IsOk() ) + m_unspecifiedAppearance.SetBgCol(bgCol); } if ( !(m_coloursCustomized & 0x0010) ) { wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); m_colPropFore = fgCol; - - // Set the cached colour as well. - ((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(fgCol); + m_propertyDefaultCell.GetData()->SetFgCol(fgCol); + if ( !m_unspecifiedAppearance.GetFgCol().IsOk() ) + m_unspecifiedAppearance.SetFgCol(fgCol); } if ( !(m_coloursCustomized & 0x0020) ) @@ -1162,39 +1439,16 @@ void wxPropertyGrid::ResetColours() bool wxPropertyGrid::SetFont( const wxFont& font ) { // Must disable active editor. - if ( m_selected ) - { - bool selRes = ClearSelection(); - wxPG_CHECK_MSG_DBG( selRes, - false, - wxT("failed to deselect a property (editor probably had invalid value)") ); - } + DoClearSelection(); - // TODO: Following code is disabled with wxMac because - // it is reported to fail. I (JMS) cannot debug it - // personally right now. -#if !defined(__WXMAC__) - bool res = wxScrolledWindow::SetFont( font ); - if ( res ) + bool res = wxControl::SetFont( font ); + if ( res && GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant { CalculateFontAndBitmapStuff( m_vspacing ); - - if ( m_pState ) - m_pState->CalculateFontAndBitmapStuff(m_vspacing); - Refresh(); } return res; -#else - // ** wxMAC Only ** - // TODO: Remove after SetFont crash fixed. - if ( m_iFlags & wxPG_FL_INITIALIZED ) - { - wxLogDebug(wxT("WARNING: propGrid.cpp: wxPropertyGrid::SetFont has been disabled on wxMac since there has been crash reported in it. If you are willing to debug the cause, replace line '#if !defined(__WXMAC__)' with line '#if 1' in wxPropertyGrid::SetFont.")); - } - return false; -#endif } // ----------------------------------------------------------------------- @@ -1222,8 +1476,8 @@ void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col ) m_colPropBack = col; m_coloursCustomized |= 0x08; - // Set the cached brush as well. - ((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(col); + m_propertyDefaultCell.GetData()->SetBgCol(col); + m_unspecifiedAppearance.SetBgCol(col); Refresh(); } @@ -1235,8 +1489,8 @@ void wxPropertyGrid::SetCellTextColour( const wxColour& col ) m_colPropFore = col; m_coloursCustomized |= 0x10; - // Set the cached colour as well. - ((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(col); + m_propertyDefaultCell.GetData()->SetFgCol(col); + m_unspecifiedAppearance.SetFgCol(col); Refresh(); } @@ -1261,191 +1515,44 @@ void wxPropertyGrid::SetCellDisabledTextColour( const wxColour& col ) // ----------------------------------------------------------------------- -void wxPropertyGrid::SetSelectionBackgroundColour( const wxColour& col ) -{ - m_colSelBack = col; - m_coloursCustomized |= 0x20; - Refresh(); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::SetSelectionTextColour( const wxColour& col ) -{ - m_colSelFore = col; - m_coloursCustomized |= 0x40; - Refresh(); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col ) -{ - m_colCapBack = col; - m_coloursCustomized |= 0x02; - Refresh(); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::SetCaptionTextColour( const wxColour& col ) -{ - m_colCapFore = col; - m_coloursCustomized |= 0x04; - - // Set the cached colour as well. - ((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(col); - - Refresh(); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::SetBackgroundColourIndex( wxPGProperty* p, int index ) -{ - unsigned char ind = index; - - p->m_bgColIndex = ind; - - unsigned int i; - for ( i=0; iGetChildCount(); i++ ) - SetBackgroundColourIndex(p->Item(i),index); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::SetPropertyBackgroundColour( wxPGPropArg id, const wxColour& colour ) -{ - wxPG_PROP_ARG_CALL_PROLOG() - - size_t i; - int colInd = -1; - - long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue()); - - // As it is most likely that the previous colour is used, start comparison - // from the end. - for ( i=(m_arrBgBrushes.size()-1); i>0; i-- ) - { - if ( ((wxPGBrush*)m_arrBgBrushes.Item(i))->GetColourAsLong() == colAsLong ) - { - colInd = i; - break; - } - } - - if ( colInd < 0 ) - { - colInd = m_arrBgBrushes.size(); - wxCHECK_RET( colInd < 256, wxT("wxPropertyGrid: Warning - Only 255 different property background colours allowed.") ); - m_arrBgBrushes.Add( (void*)new wxPGBrush(colour) ); - } - - // Set indexes - SetBackgroundColourIndex(p,colInd); - - // If this was on a visible grid, then draw it. - DrawItemAndChildren(p); -} - -// ----------------------------------------------------------------------- - -wxColour wxPropertyGrid::GetPropertyBackgroundColour( wxPGPropArg id ) const -{ - wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxColour()) - - return ((wxPGBrush*)m_arrBgBrushes.Item(p->m_bgColIndex))->GetColour(); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::SetTextColourIndex( wxPGProperty* p, int index, int flags ) +void wxPropertyGrid::SetSelectionBackgroundColour( const wxColour& col ) { - unsigned char ind = index; - - p->m_fgColIndex = ind; - - if ( p->GetChildCount() && (flags & wxPG_RECURSE) ) - { - unsigned int i; - for ( i=0; iGetChildCount(); i++ ) - SetTextColourIndex( p->Item(i), index, flags ); - } + m_colSelBack = col; + m_coloursCustomized |= 0x20; + Refresh(); } // ----------------------------------------------------------------------- -int wxPropertyGrid::CacheColour( const wxColour& colour ) +void wxPropertyGrid::SetSelectionTextColour( const wxColour& col ) { - unsigned int i; - int colInd = -1; - - long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue()); - - // As it is most likely that the previous colour is used, start comparison - // from the end. - for ( i=(m_arrFgCols.size()-1); i>0; i-- ) - { - if ( ((wxPGColour*)m_arrFgCols.Item(i))->GetColourAsLong() == colAsLong ) - { - colInd = i; - break; - } - } - - if ( colInd < 0 ) - { - colInd = m_arrFgCols.size(); - wxCHECK_MSG( colInd < 256, 0, wxT("wxPropertyGrid: Warning - Only 255 different property foreground colours allowed.") ); - m_arrFgCols.Add( (void*)new wxPGColour(colour) ); - } - - return colInd; + m_colSelFore = col; + m_coloursCustomized |= 0x40; + Refresh(); } // ----------------------------------------------------------------------- -void wxPropertyGrid::SetPropertyTextColour( wxPGPropArg id, const wxColour& colour, - bool recursively ) +void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col ) { - wxPG_PROP_ARG_CALL_PROLOG() - - if ( p->IsCategory() ) - { - wxPropertyCategory* cat = (wxPropertyCategory*) p; - cat->SetTextColIndex(CacheColour(colour)); - } + m_colCapBack = col; + m_coloursCustomized |= 0x02; - // Set indexes - int flags = 0; - if ( recursively ) - flags |= wxPG_RECURSE; - SetTextColourIndex(p, CacheColour(colour), flags); + m_categoryDefaultCell.GetData()->SetBgCol(col); - DrawItemAndChildren(p); + Refresh(); } // ----------------------------------------------------------------------- -wxColour wxPropertyGrid::GetPropertyTextColour( wxPGPropArg id ) const -{ - wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxColour()) - - return wxColour(*((wxPGColour*)m_arrFgCols.Item(p->m_fgColIndex))); -} - -void wxPropertyGrid::SetPropertyColoursToDefault( wxPGPropArg id ) +void wxPropertyGrid::SetCaptionTextColour( const wxColour& col ) { - wxPG_PROP_ARG_CALL_PROLOG() + m_colCapFore = col; + m_coloursCustomized |= 0x04; - SetBackgroundColourIndex( p, 0 ); - SetTextColourIndex( p, 0, wxPG_RECURSE ); + m_categoryDefaultCell.GetData()->SetFgCol(col); - if ( p->IsCategory() ) - { - wxPropertyCategory* cat = (wxPropertyCategory*) p; - cat->SetTextColIndex(1); - } + Refresh(); } // ----------------------------------------------------------------------- @@ -1459,26 +1566,12 @@ void wxPropertyGrid::PrepareAfterItemsAdded() m_pState->m_itemsAdded = 0; if ( m_windowStyle & wxPG_AUTO_SORT ) - Sort(); + Sort(wxPG_SORT_TOP_LEVEL_ONLY); RecalculateVirtualSize(); -} - -// ----------------------------------------------------------------------- -// wxPropertyGrid property value setting and getting -// ----------------------------------------------------------------------- - -void wxPropertyGrid::DoSetPropertyValueUnspecified( wxPGProperty* p ) -{ - m_pState->DoSetPropertyValueUnspecified(p); - DrawItemAndChildren(p); - wxPGProperty* parent = p->GetParent(); - while ( (parent->GetFlags() & wxPG_PROP_PARENTAL_FLAGS) == wxPG_PROP_MISC_PARENT ) - { - DrawItem(parent); - parent = parent->GetParent(); - } + // Fix editor position + CorrectEditorWidgetPosY(); } // ----------------------------------------------------------------------- @@ -1540,8 +1633,8 @@ bool wxPropertyGrid::EnsureVisible( wxPGPropArg id ) // Control font changer helper. void wxPropertyGrid::SetCurControlBoldFont() { - wxASSERT( m_wndEditor ); - m_wndEditor->SetFont( m_captionFont ); + wxWindow* editor = GetEditorControl(); + editor->SetFont( m_captionFont ); } // ----------------------------------------------------------------------- @@ -1589,7 +1682,7 @@ wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p, wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str ) { - if ( src_str.length() == 0 ) + if ( src_str.empty() ) { dst_str = src_str; return src_str; @@ -1601,7 +1694,7 @@ wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& sr dst_str.clear(); - for ( ; i != src_str.end(); i++ ) + for ( ; i != src_str.end(); ++i ) { wxUniChar a = *i; @@ -1648,7 +1741,7 @@ wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& sr wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str ) { - if ( src_str.length() == 0 ) + if ( src_str.empty() ) { dst_str = src_str; return src_str; @@ -1659,7 +1752,7 @@ wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& sr dst_str.clear(); - for ( ; i != src_str.end(); i++ ) + for ( ; i != src_str.end(); ++i ) { wxChar a = *i; @@ -1700,7 +1793,7 @@ wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y ) const { // Outside? if ( y < 0 ) - return (wxPGProperty*) NULL; + return NULL; unsigned int a = 0; return m_pState->m_properties->GetItemAtY(y, m_lineHeight, &a); @@ -1713,13 +1806,36 @@ wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y ) const void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); + PrepareDC(dc); + + // Don't paint after destruction has begun + if ( !HasInternalFlag(wxPG_FL_INITIALIZED) ) + return; + + // Find out where the window is scrolled to + int vx,vy; // Top left corner of client + GetViewStart(&vx,&vy); + vy *= wxPG_PIXELS_PER_UNIT; // Update everything inside the box wxRect r = GetUpdateRegion().GetBox(); - dc.SetPen(m_colEmptySpace); - dc.SetBrush(m_colEmptySpace); - dc.DrawRectangle(r); + r.y += vy; + + // FIXME: This is just a workaround for a bug that causes splitters not + // to paint when other windows are being dragged over the grid. + r.x = 0; + r.width = GetClientSize().x; + + r.y = vy; + r.height = GetClientSize().y; + + // Repaint this rectangle + DrawItems( dc, r.y, r.y + r.height, &r ); + + // We assume that the size set when grid is shown + // is what is desired. + SetInternalFlag(wxPG_FL_GOOD_SIZE_SET); } // ----------------------------------------------------------------------- @@ -1801,21 +1917,38 @@ void wxPropertyGrid::DrawExpanderButton( wxDC& dc, const wxRect& rect, // topy and bottomy are already unscrolled (ie. physical) // void wxPropertyGrid::DrawItems( wxDC& dc, - unsigned int topy, - unsigned int bottomy, - const wxRect* clipRect ) -{ - if ( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) return; + unsigned int topItemY, + unsigned int bottomItemY, + const wxRect* itemsRect ) +{ + if ( m_frozen || + m_height < 1 || + bottomItemY < topItemY || + !m_pState ) + return; m_pState->EnsureVirtualHeight(); - wxRect tempClipRect; - if ( !clipRect ) + wxRect tempItemsRect; + if ( !itemsRect ) { - tempClipRect = wxRect(0,topy,m_pState->m_width,bottomy); - clipRect = &tempClipRect; + tempItemsRect = wxRect(0, topItemY, + m_pState->m_width, + bottomItemY); + itemsRect = &tempItemsRect; } + int vx, vy; + GetViewStart(&vx, &vy); + vx *= wxPG_PIXELS_PER_UNIT; + vy *= wxPG_PIXELS_PER_UNIT; + + // itemRect is in virtual grid space + wxRect drawRect(itemsRect->x - vx, + itemsRect->y - vy, + itemsRect->width, + itemsRect->height); + // items added check if ( m_pState->m_itemsAdded ) PrepareAfterItemsAdded(); @@ -1826,14 +1959,13 @@ void wxPropertyGrid::DrawItems( wxDC& dc, wxDC* dcPtr = &dc; bool isBuffered = false; - #if wxPG_DOUBLE_BUFFER wxMemoryDC* bufferDC = NULL; if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) ) { if ( !m_doubleBuffer ) { - paintFinishY = clipRect->y; + paintFinishY = itemsRect->y; dcPtr = NULL; } else @@ -1847,66 +1979,61 @@ void wxPropertyGrid::DrawItems( wxDC& dc, isBuffered = true; } } - #endif if ( dcPtr ) { - dc.SetClippingRegion( *clipRect ); - paintFinishY = DoDrawItems( *dcPtr, NULL, NULL, clipRect, isBuffered ); + // paintFinishY and drawBottomY are in buffer/physical space + paintFinishY = DoDrawItems( *dcPtr, itemsRect, isBuffered ); + int drawBottomY = itemsRect->y + itemsRect->height - vy; + + // Clear area beyond last painted property + if ( paintFinishY < drawBottomY ) + { + dcPtr->SetPen(m_colEmptySpace); + dcPtr->SetBrush(m_colEmptySpace); + dcPtr->DrawRectangle(0, paintFinishY, + m_width, + drawBottomY ); + } } - #if wxPG_DOUBLE_BUFFER if ( bufferDC ) { - dc.Blit( clipRect->x, clipRect->y, clipRect->width, clipRect->height, - bufferDC, 0, 0, wxCOPY ); - dc.DestroyClippingRegion(); // Is this really necessary? + dc.Blit( drawRect.x, drawRect.y, drawRect.width, + drawRect.height, + bufferDC, 0, 0, wxCOPY ); delete bufferDC; } - #endif } - - // Clear area beyond bottomY? - if ( paintFinishY < (clipRect->y+clipRect->height) ) + else { + // Just clear the area dc.SetPen(m_colEmptySpace); dc.SetBrush(m_colEmptySpace); - dc.DrawRectangle( 0, paintFinishY, m_width, (clipRect->y+clipRect->height) ); + dc.DrawRectangle(drawRect); } } // ----------------------------------------------------------------------- int wxPropertyGrid::DoDrawItems( wxDC& dc, - const wxPGProperty* firstItem, - const wxPGProperty* lastItem, - const wxRect* clipRect, + const wxRect* itemsRect, bool isBuffered ) const { - // TODO: This should somehow be eliminated. - wxRect tempClipRect; - if ( !clipRect ) - { - wxASSERT(firstItem); - wxASSERT(lastItem); - tempClipRect = GetPropertyRect(firstItem, lastItem); - clipRect = &tempClipRect; - } + const wxPGProperty* firstItem; + const wxPGProperty* lastItem; - if ( !firstItem ) - firstItem = DoGetItemAtY(clipRect->y); + firstItem = DoGetItemAtY(itemsRect->y); + lastItem = DoGetItemAtY(itemsRect->y+itemsRect->height-1); if ( !lastItem ) - { - lastItem = DoGetItemAtY(clipRect->y+clipRect->height-1); - if ( !lastItem ) - lastItem = GetLastItem( wxPG_ITERATE_VISIBLE ); - } + lastItem = GetLastItem( wxPG_ITERATE_VISIBLE ); if ( m_frozen || m_height < 1 || firstItem == NULL ) - return clipRect->y; + return itemsRect->y; - wxCHECK_MSG( !m_pState->m_itemsAdded, clipRect->y, wxT("no items added") ); + wxCHECK_MSG( !m_pState->m_itemsAdded, itemsRect->y, + "no items added" ); wxASSERT( m_pState->m_properties->GetChildCount() ); int lh = m_lineHeight; @@ -1914,8 +2041,8 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, int firstItemTopY; int lastItemBottomY; - firstItemTopY = clipRect->y; - lastItemBottomY = clipRect->y + clipRect->height; + firstItemTopY = itemsRect->y; + lastItemBottomY = itemsRect->y + itemsRect->height; // Align y coordinates to item boundaries firstItemTopY -= firstItemTopY % lh; @@ -1923,19 +2050,22 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, lastItemBottomY -= 1; // Entire range outside scrolled, visible area? - if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || lastItemBottomY <= 0 ) - return clipRect->y; - - wxCHECK_MSG( firstItemTopY < lastItemBottomY, clipRect->y, wxT("invalid y values") ); + if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || + lastItemBottomY <= 0 ) + return itemsRect->y; + wxCHECK_MSG( firstItemTopY < lastItemBottomY, + itemsRect->y, + "invalid y values" ); /* - wxLogDebug(wxT(" -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"), + wxLogDebug(" -> DoDrawItems ( \"%s\" -> \"%s\" + "height=%i (ch=%i), itemsRect = 0x%lX )", firstItem->GetLabel().c_str(), lastItem->GetLabel().c_str(), (int)(lastItemBottomY - firstItemTopY), (int)m_height, - (unsigned long)clipRect ); + (unsigned long)&itemsRect ); */ wxRect r; @@ -1943,44 +2073,39 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, long windowStyle = m_windowStyle; int xRelMod = 0; - int yRelMod = 0; // - // With wxPG_DOUBLE_BUFFER, do double buffering - // - buffer's y = 0, so align cliprect and coordinates to that + // For now, do some manual calculation for double buffering + // - buffer's y = 0, so align itemsRect and coordinates to that // -#if wxPG_DOUBLE_BUFFER + // TODO: In future use wxAutoBufferedPaintDC (for example) + // + int yRelMod = 0; wxRect cr2; if ( isBuffered ) { - xRelMod = clipRect->x; - yRelMod = clipRect->y; + xRelMod = itemsRect->x; + yRelMod = itemsRect->y; // - // clipRect conversion - if ( clipRect ) - { - cr2 = *clipRect; - cr2.x -= xRelMod; - cr2.y -= yRelMod; - clipRect = &cr2; - } + // itemsRect conversion + cr2 = *itemsRect; + cr2.x -= xRelMod; + cr2.y -= yRelMod; + itemsRect = &cr2; firstItemTopY -= yRelMod; lastItemBottomY -= yRelMod; } -#else - wxUnusedVar(isBuffered); -#endif int x = m_marginWidth - xRelMod; - const wxFont& normalfont = m_font; + wxFont normalFont = GetFont(); - bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) ? true : false; + bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) != 0; - bool isEnabled = IsEnabled(); + bool isPgEnabled = IsEnabled(); // // Prepare some pens and brushes that are often changed to. @@ -1991,6 +2116,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); @@ -2004,8 +2135,9 @@ 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; + const wxArrayInt& colWidths = state->m_colWidths; #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT bool wasSelectedPainted = false; @@ -2013,7 +2145,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, // TODO: Only render columns that are within clipping region. - dc.SetFont(normalfont); + dc.SetFont(normalFont); wxPropertyGridConstIterator it( state, wxPG_ITERATE_VISIBLE, firstItem ); int endScanBottomY = lastItemBottomY + lh; @@ -2055,7 +2187,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, int rowHeight = m_fontHeight+(m_spacingy*2)+1; int textMarginHere = x; - int renderFlags = wxPGCellRenderer::Control; + int renderFlags = 0; int greyDepth = m_marginWidth; if ( !(windowStyle & wxPG_HIDE_CATEGORIES) ) @@ -2078,16 +2210,36 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, int y2 = y + lh; +#ifdef __WXMSW__ // Margin Edge - dc.DrawLine( greyDepthX, y, greyDepthX, y2 ); + // Modified by JACS to not draw a margin if wxPG_HIDE_MARGIN is specified, since it + // looks better, at least under Windows when we have a themed border (the themed-window-specific + // whitespace between the real border and the propgrid margin exacerbates the double-border look). + + // Is this or its parent themed? + bool suppressMarginEdge = (GetWindowStyle() & wxPG_HIDE_MARGIN) && + (((GetWindowStyle() & wxBORDER_MASK) == wxBORDER_THEME) || + (((GetWindowStyle() & wxBORDER_MASK) == wxBORDER_NONE) && ((GetParent()->GetWindowStyle() & wxBORDER_MASK) == wxBORDER_THEME))); +#else + bool suppressMarginEdge = false; +#endif + if (!suppressMarginEdge) + dc.DrawLine( greyDepthX, y, greyDepthX, y2 ); + else + { + // Blank out the margin edge + dc.SetPen(wxPen(GetBackgroundColour())); + dc.DrawLine( greyDepthX, y, greyDepthX, y2 ); + dc.SetPen( linepen ); + } // Splitters unsigned int si; int sx = x; - for ( si=0; sim_colWidths.size(); si++ ) + for ( si=0; sim_colWidths[si]; + sx += colWidths[si]; dc.DrawLine( sx, y, sx, y2 ); } @@ -2099,161 +2251,262 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, dc.DrawLine( greyDepthX, y2-1, gridWidth-xRelMod, y2-1 ); - if ( p == selected ) - { - renderFlags |= wxPGCellRenderer::Selected; -#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT - wasSelectedPainted = true; -#endif - } - - wxColour rowBgCol; + // + // Need to override row colours? wxColour rowFgCol; - wxBrush rowBgBrush; + wxColour rowBgCol; - if ( p->IsCategory() ) - { - if ( p->m_fgColIndex == 0 ) - rowFgCol = m_colCapFore; - else - rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex]; - rowBgBrush = wxBrush(m_colCapBack); - } - else if ( p != selected ) + bool isSelected = state->DoIsPropertySelected(p); + + if ( !isSelected ) { // Disabled may get different colour. if ( !p->IsEnabled() ) + { + renderFlags |= wxPGCellRenderer::Disabled | + wxPGCellRenderer::DontUseCellFgCol; rowFgCol = m_colDisPropFore; - else - rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex]; - - rowBgBrush = *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex]; + } } else { - // Selected gets different colour. - if ( reallyFocused ) - { - rowFgCol = m_colSelFore; - rowBgBrush = wxBrush(m_colSelBack); - } - else if ( isEnabled ) +#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT + if ( p == firstSelected ) + wasSelectedPainted = true; +#endif + + renderFlags |= wxPGCellRenderer::Selected; + + if ( !p->IsCategory() ) { - rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex]; - rowBgBrush = marginBrush; + renderFlags |= wxPGCellRenderer::DontUseCellFgCol | + wxPGCellRenderer::DontUseCellBgCol; + + if ( reallyFocused && p == firstSelected ) + { + rowFgCol = m_colSelFore; + rowBgCol = selBackCol; + } + else if ( isPgEnabled ) + { + rowFgCol = m_colPropFore; + if ( p == firstSelected ) + rowBgCol = m_colMargin; + else + rowBgCol = selBackCol; + } + else + { + rowFgCol = m_colDisPropFore; + rowBgCol = selBackCol; + } } - else + } + + wxBrush rowBgBrush; + + if ( rowBgCol.IsOk() ) + rowBgBrush = wxBrush(rowBgCol); + + if ( HasInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL) ) + renderFlags = renderFlags & ~wxPGCellRenderer::DontUseCellColours; + + // + // Fill additional margin area with background colour of first cell + if ( greyDepthX < textMarginHere ) + { + if ( !(renderFlags & wxPGCellRenderer::DontUseCellBgCol) ) { - rowFgCol = m_colDisPropFore; - rowBgBrush = wxBrush(m_colSelBack); + wxPGCell& cell = p->GetCell(0); + rowBgCol = cell.GetBgCol(); + rowBgBrush = wxBrush(rowBgCol); } + dc.SetBrush(rowBgBrush); + dc.SetPen(rowBgCol); + dc.DrawRectangle(greyDepthX+1, y, + textMarginHere-greyDepthX, lh-1); } bool fontChanged = false; + // Expander button rectangle wxRect butRect( ((p->m_depth - 1) * m_subgroup_extramargin) - xRelMod, y, m_marginWidth, lh ); - if ( p->IsCategory() ) + // Default cell rect fill the entire row + wxRect cellRect(greyDepthX, y, + gridWidth - greyDepth + 2, rowHeight-1 ); + + bool isCategory = p->IsCategory(); + + if ( isCategory ) { - // Captions are all cells merged as one dc.SetFont(m_captionFont); fontChanged = true; - wxRect cellRect(greyDepthX, y, gridWidth - greyDepth + 2, rowHeight-1 ); - - dc.SetBrush(rowBgBrush); - dc.SetPen(rowBgBrush.GetColour()); - dc.SetTextForeground(rowFgCol); - - dc.DrawRectangle(cellRect); - // Foreground - wxPGCellRenderer* renderer = p->GetCellRenderer(0); - renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags ); + if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol ) + { + dc.SetBrush(rowBgBrush); + dc.SetPen(rowBgCol); + } - // Tree Item Button - if ( !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) - DrawExpanderButton( dc, butRect, p ); + if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol ) + { + dc.SetTextForeground(rowFgCol); + } } else { - if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) ) + // Fine tune button rectangle to actually fit the cell + if ( butRect.x > 0 ) + butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST; + + if ( p->m_flags & wxPG_PROP_MODIFIED && + (windowStyle & wxPG_BOLD_MODIFIED) ) { dc.SetFont(m_captionFont); fontChanged = true; } - unsigned int ci; - int cellX = x + 1; - int nextCellWidth = state->m_colWidths[0]; - wxRect cellRect(greyDepthX+1, y, 0, rowHeight-1); - int textXAdd = textMarginHere - greyDepthX; + // Magic fine-tuning for non-category rows + cellRect.x += 1; + } + + int firstCellWidth = colWidths[0] - (greyDepthX - m_marginWidth); + int firstCellX = cellRect.x; + + // Calculate cellRect.x for the last cell + unsigned int ci = 0; + int cellX = x + 1; + for ( ci=0; cim_colWidths.size(); ci++ ) + // Merge with column to the right? + if ( !prevFilled && isCategory ) { - cellRect.width = nextCellWidth - 1; + cellRect.width += colWidths[ci+1]; + } - bool ctrlCell = false; + if ( !isCategory ) + cellRect.width -= 1; - // Background - if ( p == selected && m_wndEditor && ci == 1 ) + wxWindow* cellEditor = NULL; + int cellRenderFlags = renderFlags; + + // 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 || ci == m_selColumn) ) + { + if ( p == firstSelected ) { - wxColour editorBgCol = GetEditorControl()->GetBackgroundColour(); + if ( ci == 1 && m_wndEditor ) + cellEditor = m_wndEditor; + else if ( ci == m_selColumn && m_labelEditor ) + cellEditor = m_labelEditor; + } + + if ( cellEditor ) + { + wxColour editorBgCol = + 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 + { + dc.SetBrush(m_colPropBack); + dc.SetPen(m_colPropBack); + if ( p->IsEnabled() ) + dc.SetTextForeground(m_colPropFore); + else + dc.SetTextForeground(m_colDisPropFore); + } + } + else + { + if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol ) { dc.SetBrush(rowBgBrush); - dc.SetPen(rowBgBrush.GetColour()); - dc.SetTextForeground(rowFgCol); + dc.SetPen(rowBgCol); } - dc.DrawRectangle(cellRect); - - // Tree Item Button - if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) - DrawExpanderButton( dc, butRect, p ); + if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol ) + { + dc.SetTextForeground(rowFgCol); + } + } - dc.SetClippingRegion(cellRect); + dc.SetClippingRegion(cellRect); - cellRect.x += textXAdd; - cellRect.width -= textXAdd; + cellRect.x += textXAdd; + cellRect.width -= textXAdd; - // Foreground - if ( !ctrlCell ) + // Foreground + if ( !cellEditor ) + { + wxPGCellRenderer* renderer; + int cmnVal = p->GetCommonValue(); + if ( cmnVal == -1 || ci != 1 ) { - wxPGCellRenderer* renderer; - int cmnVal = p->GetCommonValue(); - if ( cmnVal == -1 || ci != 1 ) - { - renderer = p->GetCellRenderer(ci); - renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags ); - } - else - { - renderer = GetCommonValue(cmnVal)->GetRenderer(); - renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags ); - } + renderer = p->GetCellRenderer(ci); + prevFilled = renderer->Render(dc, cellRect, this, + p, ci, -1, + cellRenderFlags ); } - - cellX += state->m_colWidths[ci]; - if ( ci < (state->m_colWidths.size()-1) ) - nextCellWidth = state->m_colWidths[ci+1]; - cellRect.x = cellX; - dc.DestroyClippingRegion(); // Is this really necessary? - textXAdd = 0; + else + { + renderer = GetCommonValue(cmnVal)->GetRenderer(); + prevFilled = renderer->Render(dc, cellRect, this, + p, ci, -1, + cellRenderFlags ); + } + } + else + { + prevFilled = true; } + + dc.DestroyClippingRegion(); // Is this really necessary? } + while ( ci > 0 ); if ( fontChanged ) - dc.SetFont(normalfont); + dc.SetFont(normalFont); y += rowHeight; } @@ -2270,7 +2523,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, } #endif - return y + yRelMod; + return y; } // ----------------------------------------------------------------------- @@ -2281,13 +2534,15 @@ wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProper if ( m_width < 10 || m_height < 10 || !m_pState->m_properties->GetChildCount() || - p1 == (wxPGProperty*) NULL ) + p1 == NULL ) return wxRect(0,0,0,0); int vy = 0; // // Return rect which encloses the given property range + // (in logical grid coordinates) + // int visTop = p1->GetY(); int visBottom; @@ -2298,7 +2553,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(); @@ -2330,7 +2585,14 @@ void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 ) wxRect r = GetPropertyRect(p1, p2); if ( r.width > 0 ) { - m_canvas->RefreshRect(r); + // Convert rectangle from logical grid coordinates to physical ones + int vx, vy; + GetViewStart(&vx, &vy); + vx *= wxPG_PIXELS_PER_UNIT; + vy *= wxPG_PIXELS_PER_UNIT; + r.x -= vx; + r.y -= vy; + RefreshRect(r); } } @@ -2338,8 +2600,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) || p->IsChildSelected(true) ) + { + // NB: We must copy the selection. + wxArrayPGProperty selection = m_pState->m_selection; + DoSetSelection(selection, wxPG_SEL_FORCE); + } DrawItemAndChildren(p); } @@ -2377,11 +2643,10 @@ void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p ) if ( m_pState->m_itemsAdded || m_frozen ) return; - wxWindow* wndPrimary = GetEditorControl(); - // Update child control. - if ( m_selected && m_selected->GetParent() == p ) - m_selected->UpdateControl(wndPrimary); + wxPGProperty* selected = GetSelection(); + if ( selected && selected->GetParent() == p ) + RefreshEditor(); const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem(); @@ -2395,10 +2660,7 @@ void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground), { PrepareAfterItemsAdded(); - wxWindow::Refresh(false); - if ( m_canvas ) - // TODO: Coordinate translation - m_canvas->Refresh(false, rect); + wxWindow::Refresh(false, rect); #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT // I think this really helps only GTK+1.2 @@ -2413,13 +2675,6 @@ void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground), void wxPropertyGrid::Clear() { - if ( m_selected ) - { - bool selRes = DoSelectProperty(NULL, wxPG_SEL_DELETING); // This must be before state clear - wxPG_CHECK_RET_DBG( selRes, - wxT("failed to deselect a property (editor probably had invalid value)") ); - } - m_pState->DoClear(); m_propHover = NULL; @@ -2437,8 +2692,7 @@ void wxPropertyGrid::Clear() bool wxPropertyGrid::EnableCategories( bool enable ) { - if ( !ClearSelection() ) - return false; + DoClearSelection(); if ( enable ) { @@ -2488,17 +2742,13 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) if ( pNewState == m_pState ) return; - wxPGProperty* oldSelection = m_selected; + wxArrayPGProperty oldSelection = m_pState->m_selection; - // Deselect - if ( m_selected ) - { - bool selRes = ClearSelection(); - wxPG_CHECK_RET_DBG( selRes, - wxT("failed to deselect a property (editor probably had invalid value)") ); - } + // 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(); @@ -2520,13 +2770,14 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) { // // Just in case, fully re-center splitter - if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) ) - pNewState->m_fSplitterX = -1.0; + //if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) ) + // pNewState->m_fSplitterX = -1.0; - pNewState->OnClientWidthChange( pgWidth, pgWidth - pNewState->m_width ); + pNewState->OnClientWidthChange(pgWidth, + pgWidth - pNewState->m_width); } - m_propHover = (wxPGProperty*) NULL; + m_propHover = NULL; // If necessary, convert state to correct mode. if ( orig_mode != new_state_mode ) @@ -2537,12 +2788,11 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) else if ( !m_frozen ) { // Refresh, if not frozen. - if ( m_pState->m_itemsAdded ) - PrepareAfterItemsAdded(); + 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(); @@ -2553,53 +2803,52 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) // ----------------------------------------------------------------------- -void wxPropertyGrid::SortChildren( wxPGPropArg id ) -{ - wxPG_PROP_ARG_CALL_PROLOG() - - m_pState->SortChildren( p ); -} - -// ----------------------------------------------------------------------- - -void wxPropertyGrid::Sort() -{ - bool selRes = ClearSelection(); // This must be before state clear - wxPG_CHECK_RET_DBG( selRes, - wxT("failed to deselect a property (editor probably had invalid value)") ); - - m_pState->Sort(); -} - -// ----------------------------------------------------------------------- - // Call to SetSplitterPosition will always disable splitter auto-centering // if parent window is shown. -void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int splitterIndex, bool allPages ) +void wxPropertyGrid::DoSetSplitterPosition( int newxpos, + int splitterIndex, + int flags ) { if ( ( newxpos < wxPG_DRAG_MARGIN ) ) return; wxPropertyGridPageState* state = m_pState; - state->DoSetSplitterPosition( newxpos, splitterIndex, allPages ); + if ( flags & wxPG_SPLITTER_FROM_EVENT ) + state->m_dontCenterSplitter = true; - if ( refresh ) + state->DoSetSplitterPosition(newxpos, splitterIndex, flags); + + if ( flags & wxPG_SPLITTER_REFRESH ) { - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); Refresh(); } + + return; +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing ) +{ + wxPropertyGridPageState* state = m_pState; + if ( state ) + state->ResetColumnSizes(0); + + if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) ) + m_pState->m_dontCenterSplitter = false; } // ----------------------------------------------------------------------- -void wxPropertyGrid::CenterSplitter( bool enableAutoCentering ) +void wxPropertyGrid::CenterSplitter( bool enableAutoResizing ) { - SetSplitterPosition( m_width/2, true ); - if ( enableAutoCentering && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) ) - m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER); + SetSplitterPosition( m_width/2 ); + if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) ) + m_pState->m_dontCenterSplitter = false; } // ----------------------------------------------------------------------- @@ -2649,7 +2898,7 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) // Don't do this if already processing editor event. It might // induce recursive dialogs and crap like that. - if ( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT ) + if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT ) { if ( m_inDoPropertyChanged ) return true; @@ -2657,14 +2906,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; + m_inCommitChangesFromEditor = true; - wxVariant variant(m_selected->GetValueRef()); + wxVariant variant(selected->GetValueRef()); bool valueIsPending = false; // JACS - necessary to avoid new focus being found spuriously within OnIdle @@ -2677,10 +2928,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; } @@ -2694,9 +2948,9 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) EditorsValueWasNotModified(); } - bool res = true; + m_inCommitChangesFromEditor = false; - m_inCommitChangesFromEditor = 0; + bool res = true; if ( validationFailure && !forceSuccess ) { @@ -2706,18 +2960,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(); } @@ -2729,7 +2983,8 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) // ----------------------------------------------------------------------- -bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue ) +bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue, + int flags ) { // // Runs all validation functionality. @@ -2737,8 +2992,12 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue // m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior; + m_validationInfo.m_isFailing = true; - if ( pendingValue.GetType() == wxPG_VARIANT_TYPE_LIST ) + // + // Variant list a special value that cannot be validated + // by normal means. + if ( pendingValue.GetType() != wxPG_VARIANT_TYPE_LIST ) { if ( !p->ValidateValue(pendingValue, m_validationInfo) ) return false; @@ -2798,34 +3057,37 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue wxVariant evtChangingValue = value; - // FIXME: After proper ValueToString()s added, remove - // this. It is just a temporary fix, as evt_changing - // will simply not work for wxPG_PROP_COMPOSED_VALUE - // (unless it is selected, and textctrl editor is open). - if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) ) - { - evtChangingProperty = baseChangedProperty; - if ( evtChangingProperty != p ) - { - evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue ); - } - else - { - evtChangingValue = pendingValue; - } - } - - if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) ) + if ( flags & SendEvtChanging ) { - if ( changedProperty == m_selected ) + // FIXME: After proper ValueToString()s added, remove + // this. It is just a temporary fix, as evt_changing + // will simply not work for wxPG_PROP_COMPOSED_VALUE + // (unless it is selected, and textctrl editor is open). + if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) ) { - wxWindow* editor = GetEditorControl(); - wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) ); - evtChangingValue = wxStaticCast(editor, wxTextCtrl)->GetValue(); + evtChangingProperty = baseChangedProperty; + if ( evtChangingProperty != p ) + { + evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue ); + } + else + { + evtChangingValue = pendingValue; + } } - else + + if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) ) { - wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value.")); + if ( changedProperty == GetSelection() ) + { + wxWindow* editor = GetEditorControl(); + wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) ); + evtChangingValue = wxStaticCast(editor, wxTextCtrl)->GetValue(); + } + else + { + wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value.")); + } } } @@ -2847,45 +3109,127 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue return false; } - // SendEvent returns true if event was vetoed - if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue, 0 ) ) - return false; + if ( flags & SendEvtChanging ) + { + // SendEvent returns true if event was vetoed + if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, + &evtChangingValue ) ) + return false; + } + + if ( flags & IsStandaloneValidation ) + { + // If called in 'generic' context, we need to reset + // m_chgInfo_changedProperty and write back translated value. + m_chgInfo_changedProperty = NULL; + pendingValue = value; + } + + m_validationInfo.m_isFailing = false; return true; } // ----------------------------------------------------------------------- +#if wxUSE_STATUSBAR +wxStatusBar* wxPropertyGrid::GetStatusBar() +{ + wxWindow* topWnd = ::wxGetTopLevelParent(this); + if ( topWnd && topWnd->IsKindOf(CLASSINFO(wxFrame)) ) + { + wxFrame* pFrame = wxStaticCast(topWnd, wxFrame); + if ( pFrame ) + return pFrame->GetStatusBar(); + } + return NULL; +} +#endif + +// ----------------------------------------------------------------------- + void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), const wxString& msg ) { - if ( !msg.length() ) + if ( msg.empty() ) return; #if wxUSE_STATUSBAR if ( !wxPGGlobalVars->m_offline ) { - wxWindow* topWnd = ::wxGetTopLevelParent(this); - if ( topWnd ) + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) { - wxFrame* pFrame = wxDynamicCast(topWnd, wxFrame); - if ( pFrame ) - { - wxStatusBar* pStatusBar = pFrame->GetStatusBar(); - if ( pStatusBar ) - { - pStatusBar->SetStatusText(msg); - return; - } - } + pStatusBar->SetStatusText(msg); + return; } } #endif - ::wxMessageBox(msg, _T("Property Error")); + ::wxMessageBox(msg, _("Property Error")); +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::DoHidePropertyError( wxPGProperty* WXUNUSED(property) ) +{ +#if wxUSE_STATUSBAR + if ( !wxPGGlobalVars->m_offline ) + { + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) + { + pStatusBar->SetStatusText(wxEmptyString); + return; + } + } +#endif } // ----------------------------------------------------------------------- +bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property, + wxVariant& invalidValue ) +{ + if ( m_inOnValidationFailure ) + return true; + + m_inOnValidationFailure = true; + wxON_BLOCK_EXIT_SET(m_inOnValidationFailure, false); + + wxWindow* editor = GetEditorControl(); + int vfb = m_validationInfo.m_failureBehavior; + + if ( m_inDoSelectProperty ) + { + // When property selection is being changed, do not display any + // messages, if some were already shown for this property. + if ( property->HasFlag(wxPG_PROP_INVALID_VALUE) ) + { + m_validationInfo.m_failureBehavior = + vfb & ~(wxPG_VFB_SHOW_MESSAGE | + wxPG_VFB_SHOW_MESSAGEBOX | + wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR); + } + } + + // First call property's handler + property->OnValidationFailure(invalidValue); + + bool res = DoOnValidationFailure(property, invalidValue); + + // + // For non-wxTextCtrl editors, we do need to revert the value + if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) && + property == GetSelection() ) + { + property->GetEditorClass()->UpdateControl(property, editor); + } + + property->SetFlag(wxPG_PROP_INVALID_VALUE); + + return res; +} + bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& WXUNUSED(invalidValue) ) { int vfb = m_validationInfo.m_failureBehavior; @@ -2896,40 +3240,64 @@ bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& W if ( (vfb & wxPG_VFB_MARK_CELL) && !property->HasFlag(wxPG_PROP_INVALID_VALUE) ) { - wxASSERT_MSG( !property->GetCell(0) && !property->GetCell(1), - wxT("Currently wxPG_VFB_MARK_CELL only works with properties with standard first two cells") ); + unsigned int colCount = m_pState->GetColumnCount(); + + // We need backup marked property's cells + m_propCellsBackup = property->m_cells; + + wxColour vfbFg = *wxWHITE; + wxColour vfbBg = *wxRED; + + property->EnsureCells(colCount); - if ( !property->GetCell(0) && !property->GetCell(1) ) + for ( unsigned int i=0; iSetCell(0, new wxPGCell(property->GetLabel(), wxNullBitmap, vfbFg, vfbBg)); - property->SetCell(1, new wxPGCell(property->GetDisplayedString(), wxNullBitmap, vfbFg, vfbBg)); + wxPGCell& cell = property->m_cells[i]; + cell.SetFgCol(vfbFg); + cell.SetBgCol(vfbBg); + } - DrawItemAndChildren(property); + DrawItemAndChildren(property); - if ( property == m_selected ) - { - SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL); + if ( property == GetSelection() ) + { + SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL); - wxWindow* editor = GetEditorControl(); - if ( editor ) - { - editor->SetForegroundColour(vfbFg); - editor->SetBackgroundColour(vfbBg); - } + wxWindow* editor = GetEditorControl(); + if ( editor ) + { + editor->SetForegroundColour(vfbFg); + editor->SetBackgroundColour(vfbBg); } } } - if ( vfb & wxPG_VFB_SHOW_MESSAGE ) + if ( vfb & (wxPG_VFB_SHOW_MESSAGE | + wxPG_VFB_SHOW_MESSAGEBOX | + wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR) ) { wxString msg = m_validationInfo.m_failureMessage; - if ( !msg.length() ) - msg = _T("You have entered invalid value. Press ESC to cancel editing."); + if ( msg.empty() ) + msg = _("You have entered invalid value. Press ESC to cancel editing."); + + #if wxUSE_STATUSBAR + if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) + { + if ( !wxPGGlobalVars->m_offline ) + { + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) + pStatusBar->SetStatusText(msg); + } + } + #endif + + if ( vfb & wxPG_VFB_SHOW_MESSAGE ) + DoShowPropertyError(property, msg); - DoShowPropertyError(property, msg); + if ( vfb & wxPG_VFB_SHOW_MESSAGEBOX ) + ::wxMessageBox(msg, _("Property Error")); } return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true; @@ -2943,12 +3311,12 @@ void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property ) if ( vfb & wxPG_VFB_MARK_CELL ) { - property->SetCell(0, NULL); - property->SetCell(1, NULL); + // Revert cells + property->m_cells = m_propCellsBackup; 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); @@ -2958,6 +3326,25 @@ void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property ) DrawItemAndChildren(property); } } + +#if wxUSE_STATUSBAR + if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) + { + if ( !wxPGGlobalVars->m_offline ) + { + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) + pStatusBar->SetStatusText(wxEmptyString); + } + } +#endif + + if ( vfb & wxPG_VFB_SHOW_MESSAGE ) + { + DoHidePropertyError(property); + } + + m_validationInfo.m_isFailing = false; } // ----------------------------------------------------------------------- @@ -2968,11 +3355,15 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) if ( m_inDoPropertyChanged ) return true; - wxWindow* editor = GetEditorControl(); + m_inDoPropertyChanged = true; + wxON_BLOCK_EXIT_SET(m_inDoPropertyChanged, false); + + wxPGProperty* selected = GetSelection(); m_pState->m_anyModified = 1; - m_inDoPropertyChanged = 1; + // If property's value is being changed, assume it is valid + OnValidationFailureReset(selected); // Maybe need to update control wxASSERT( m_chgInfo_changedProperty != NULL ); @@ -2991,11 +3382,15 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) changedProperty->SetValue(value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER); + // NB: Call GetEditorControl() as late as possible, because OnSetValue() + // and perhaps other user-defined virtual functions may change it. + wxWindow* editor = GetEditorControl(); + // Set as Modified (not if dragging just began) 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(); @@ -3012,7 +3407,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(); @@ -3030,8 +3425,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) // control. if ( selFlags & wxPG_SEL_DIALOGVAL ) { - if ( editor ) - p->GetEditorClass()->UpdateControl(p, editor); + RefreshEditor(); } else { @@ -3052,14 +3446,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 ); - - m_inDoPropertyChanged = 0; + SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL ); return true; } @@ -3087,79 +3479,143 @@ bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id, wxVariant newValue ) // ----------------------------------------------------------------------- -// Runs wxValidator for the selected property -bool wxPropertyGrid::DoEditorValidate() +wxVariant wxPropertyGrid::GetUncommittedPropertyValue() { - return true; + wxPGProperty* prop = GetSelectedProperty(); + + if ( !prop ) + return wxNullVariant; + + wxTextCtrl* tc = GetEditorTextCtrl(); + wxVariant value = prop->GetValue(); + + if ( !tc || !IsEditorsValueModified() ) + return value; + + if ( !prop->StringToValue(value, tc->GetValue()) ) + return value; + + if ( !PerformValidation(prop, value, IsStandaloneValidation) ) + return prop->GetValue(); + + return value; } // ----------------------------------------------------------------------- -bool wxPropertyGrid::ProcessEvent(wxEvent& event) +// Runs wxValidator for the selected property +bool wxPropertyGrid::DoEditorValidate() { - wxWindow* wnd = (wxWindow*) event.GetEventObject(); - if ( wnd && wnd->IsKindOf(CLASSINFO(wxWindow)) ) +#if wxUSE_VALIDATORS + wxRecursionGuard guard(m_validatingEditor); + if ( guard.IsInside() ) + return false; + + wxPGProperty* selected = GetSelection(); + if ( selected ) { - wxWindow* parent = wnd->GetParent(); + wxWindow* wnd = GetEditorControl(); - if ( parent && - (parent == m_canvas || - parent->GetParent() == m_canvas) ) + wxValidator* validator = selected->GetValidator(); + if ( validator && wnd ) { - OnCustomEditorEvent((wxCommandEvent&)event); - return true; + validator->SetWindow(wnd); + if ( !validator->Validate(this) ) + return false; } } - return wxPanel::ProcessEvent(event); +#endif + return true; } // ----------------------------------------------------------------------- -// NB: It may really not be wxCommandEvent - must check if necessary -// (usually not). -void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) +bool wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) { - wxPGProperty* selected = m_selected; + // + // NB: We should return true if the event was recognized as + // a dedicated wxPropertyGrid event, and as such was + // either properly handled or ignored. + // + + // It is possible that this handler receives event even before + // the control has been properly initialized. Let's skip the + // event handling in that case. + if ( !m_pState ) + return false; + + // Don't care about the event if it originated from the + // 'label editor'. In this function we only care about the + // property value editor. + if ( m_labelEditor && event.GetId() == m_labelEditor->GetId() ) + { + event.Skip(); + return true; + } + + wxPGProperty* selected = GetSelection(); // Somehow, event is handled after property has been deselected. // Possibly, but very rare. - if ( !selected ) - return; + if ( !selected || + selected->HasFlag(wxPG_PROP_BEING_DELETED) || + m_inOnValidationFailure || + // Also don't handle editor event if wxEVT_PG_CHANGED or + // similar is currently doing something (showing a + // message box, for instance). + m_processedEvent ) + return true; - if ( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT ) - return; + if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT ) + return true; wxVariant pendingValue(selected->GetValueRef()); wxWindow* wnd = GetEditorControl(); + wxWindow* editorWnd = wxDynamicCast(event.GetEventObject(), wxWindow); int selFlags = 0; bool wasUnspecified = selected->IsValueUnspecified(); int usesAutoUnspecified = selected->UsesAutoUnspecified(); - bool valueIsPending = false; m_chgInfo_changedProperty = NULL; - m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED|wxPG_FL_VALUE_CHANGE_IN_EVENT); + m_iFlags &= ~wxPG_FL_VALUE_CHANGE_IN_EVENT; // // Filter out excess wxTextCtrl modified events - if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && - wnd && - wnd->IsKindOf(CLASSINFO(wxTextCtrl)) ) + if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd ) { - wxTextCtrl* tc = (wxTextCtrl*) wnd; + if ( wnd->IsKindOf(CLASSINFO(wxTextCtrl)) ) + { + wxTextCtrl* tc = (wxTextCtrl*) wnd; - wxString newTcValue = tc->GetValue(); - if ( m_prevTcValue == newTcValue ) - return; + wxString newTcValue = tc->GetValue(); + if ( m_prevTcValue == newTcValue ) + return true; + m_prevTcValue = newTcValue; + } + else if ( wnd->IsKindOf(CLASSINFO(wxComboCtrl)) ) + { + // In some cases we might stumble unintentionally on + // wxComboCtrl's embedded wxTextCtrl's events. Let's + // avoid them. + if ( editorWnd->IsKindOf(CLASSINFO(wxTextCtrl)) ) + return false; + + wxComboCtrl* cc = (wxComboCtrl*) wnd; - m_prevTcValue = newTcValue; + wxString newTcValue = cc->GetTextCtrl()->GetValue(); + if ( m_prevTcValue == newTcValue ) + return true; + m_prevTcValue = newTcValue; + } } - SetInternalFlag(wxPG_FL_IN_ONCUSTOMEDITOREVENT); + SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT); bool validationFailure = false; bool buttonWasHandled = false; + bool result = false; // // Try common button handling @@ -3180,18 +3636,29 @@ void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) if ( !buttonWasHandled ) { - if ( wnd ) + if ( wnd || m_wndEditor2 ) { // First call editor class' event handler. const wxPGEditor* editor = selected->GetEditorClass(); - if ( editor->OnEvent( this, selected, wnd, event ) ) + if ( editor->OnEvent( this, selected, editorWnd, event ) ) { + result = true; + // If changes, validate them if ( DoEditorValidate() ) { - if ( editor->GetValueFromControl( pendingValue, m_selected, wnd ) ) + if ( editor->GetValueFromControl( pendingValue, + selected, + wnd ) ) valueIsPending = true; + + // Mark value always as pending if validation is currently + // failing and value was not unspecified + if ( !valueIsPending && + !pendingValue.IsNull() && + m_validationInfo.m_isFailing ) + valueIsPending = true; } else { @@ -3203,7 +3670,7 @@ void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) // Then the property's custom handler (must be always called, unless // validation failed). if ( !validationFailure ) - buttonWasHandled = selected->OnEvent( this, wnd, event ); + buttonWasHandled = selected->OnEvent( this, editorWnd, event ); } // SetValueInEvent(), as called in one of the functions referred above @@ -3216,7 +3683,7 @@ void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) } if ( !validationFailure && valueIsPending ) - if ( !PerformValidation(m_selected, pendingValue) ) + if ( !PerformValidation(selected, pendingValue) ) validationFailure = true; if ( validationFailure) @@ -3240,7 +3707,7 @@ void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) else { // No value after all - + // Regardless of editor type, unfocus editor on // text-editing related enter press. if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER ) @@ -3251,12 +3718,15 @@ void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) // Let unhandled button click events go to the parent if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED ) { + result = true; wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId()); GetEventHandler()->AddPendingEvent(evt); } } - ClearInternalFlag(wxPG_FL_IN_ONCUSTOMEDITOREVENT); + ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT); + + return result; } // ----------------------------------------------------------------------- @@ -3266,25 +3736,36 @@ void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const { int itemy = p->GetY2(m_lineHeight); - int vy = 0; - int cust_img_space = 0; int splitterX = m_pState->DoGetSplitterPosition(column-1); int colEnd = splitterX + m_pState->m_colWidths[column]; + int imageOffset = 0; - // TODO: If custom image detection changes from current, change this. - if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE /*p->m_flags & wxPG_PROP_CUSTOMIMAGE*/ ) + int vx, vy; // Top left corner of client + GetViewStart(&vx, &vy); + vy *= wxPG_PIXELS_PER_UNIT; + + if ( column == 1 ) + { + // TODO: If custom image detection changes from current, change this. + if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE ) + { + //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; + int iw = p->OnMeasureImage().x; + if ( iw < 1 ) + iw = wxPG_CUSTOM_IMAGE_WIDTH; + imageOffset = p->GetImageOffset(iw); + } + } + else if ( column == 0 ) { - //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; - int imwid = p->OnMeasureImage().x; - if ( imwid < 1 ) imwid = wxPG_CUSTOM_IMAGE_WIDTH; - cust_img_space = imwid + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2; + splitterX += (p->m_depth - 1) * m_subgroup_extramargin; } return wxRect ( - splitterX+cust_img_space+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1, + splitterX+imageOffset+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1, itemy-vy, - colEnd-splitterX-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-cust_img_space-1, + colEnd-splitterX-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-imageOffset-1, m_lineHeight-1 ); } @@ -3375,15 +3856,73 @@ void wxPropertyGrid::CustomSetCursor( int type, bool override ) if ( type == wxCURSOR_SIZEWE ) cursor = m_cursorSizeWE; - m_canvas->SetCursor( *cursor ); + SetCursor( *cursor ); m_curcursor = type; } // ----------------------------------------------------------------------- -// wxPropertyGrid property selection + +wxString +wxPropertyGrid::GetUnspecifiedValueText( int argFlags ) const +{ + const wxPGCell& ua = GetUnspecifiedValueAppearance(); + + if ( ua.HasText() && + !(argFlags & wxPG_FULL_VALUE) && + !(argFlags & wxPG_EDITABLE_VALUE) ) + return ua.GetText(); + + return wxEmptyString; +} + +// ----------------------------------------------------------------------- +// wxPropertyGrid property selection, editor creation // ----------------------------------------------------------------------- +// +// This class forwards events from property editor controls to wxPropertyGrid. +class wxPropertyGridEditorEventForwarder : public wxEvtHandler +{ +public: + wxPropertyGridEditorEventForwarder( wxPropertyGrid* propGrid ) + : wxEvtHandler(), m_propGrid(propGrid) + { + } + + virtual ~wxPropertyGridEditorEventForwarder() + { + } + +private: + bool ProcessEvent( wxEvent& event ) + { + // Always skip + event.Skip(); + + m_propGrid->HandleCustomEditorEvent(event); + + // + // NB: We should return true if the event was recognized as + // a dedicated wxPropertyGrid event, and as such was + // either properly handled or ignored. + // + if ( m_propGrid->IsMainButtonEvent(event) ) + return true; + + // + // NB: On wxMSW, a wxTextCtrl with wxTE_PROCESS_ENTER + // may beep annoyingly if that event is skipped + // and passed to parent event handler. + if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER ) + return true; + + return wxEvtHandler::ProcessEvent(event); + } + + wxPropertyGrid* m_propGrid; +}; + // Setups event handling for child control void wxPropertyGrid::SetupChildEventHandling( wxWindow* argWnd ) { @@ -3409,88 +3948,128 @@ void wxPropertyGrid::SetupChildEventHandling( wxWindow* argWnd ) argWnd->Connect(id, wxEVT_LEAVE_WINDOW, wxMouseEventHandler(wxPropertyGrid::OnMouseEntry), NULL, this); - argWnd->Connect(id, wxEVT_KEY_DOWN, - wxCharEventHandler(wxPropertyGrid::OnChildKeyDown), - NULL, this); } + + wxPropertyGridEditorEventForwarder* forwarder; + forwarder = new wxPropertyGridEditorEventForwarder(this); + argWnd->PushEventHandler(forwarder); + + argWnd->Connect(id, wxEVT_KEY_DOWN, + wxCharEventHandler(wxPropertyGrid::OnChildKeyDown), + 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() { + // + // Return focus back to canvas from children (this is required at least for + // GTK+, which, unlike Windows, clears focus when control is destroyed + // instead of moving it to closest parent). + SetFocusOnCanvas(); + // Do not free editors immediately if processing events if ( m_wndEditor2 ) { + wxEvtHandler* handler = m_wndEditor2->PopEventHandler(false); m_wndEditor2->Hide(); - wxPendingDelete.Append( m_wndEditor2 ); - m_wndEditor2 = (wxWindow*) NULL; + wxPendingDelete.Append( handler ); + DestroyEditorWnd(m_wndEditor2); + m_wndEditor2 = NULL; } if ( m_wndEditor ) { + wxEvtHandler* handler = m_wndEditor->PopEventHandler(false); m_wndEditor->Hide(); - wxPendingDelete.Append( m_wndEditor ); - m_wndEditor = (wxWindow*) NULL; + wxPendingDelete.Append( handler ); + DestroyEditorWnd(m_wndEditor); + m_wndEditor = NULL; } } // Call with NULL to de-select property bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) { - wxPanel* canvas = GetPanel(); - /* if (p) + { wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(), p->m_parent->m_label.c_str(),p->GetIndexInParent()); + } else + { wxLogDebug(wxT("SelectProperty( NULL, -1 )")); + } */ if ( m_inDoSelectProperty ) return true; - m_inDoSelectProperty = 1; - - wxPGProperty* prev = m_selected; + m_inDoSelectProperty = true; + wxON_BLOCK_EXIT_SET(m_inDoSelectProperty, false); 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; + + // Always send event, as this is indirect call + DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); /* - 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" ); - + if (p) wxPrintf( "P = %s\n", p->GetClassInfo()->GetClassName() ); else wxPrintf( "P = NULL\n" ); */ - + + wxWindow* primaryCtrl = NULL; + // If we are frozen, then just set the values. if ( m_frozen ) { 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. FreeEditors(); // Prevent any further selection measures in this call - p = (wxPGProperty*) NULL; + p = NULL; } 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 ) @@ -3509,38 +4088,31 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } } - m_inDoSelectProperty = 0; return true; } // // First, deactivate previous - if ( m_selected ) + if ( prevFirstSel ) { - - OnValidationFailureReset(m_selected); - // Must double-check if this is an selected in case of forceswitch - if ( p != prev ) + if ( p != prevFirstSel ) { if ( !CommitChangesFromEditor(flags) ) { // Validation has failed, so we can't exit the previous editor //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."), // _("Invalid Value"),wxOK|wxICON_ERROR); - m_inDoSelectProperty = 0; return false; } } - FreeEditors(); - m_selColumn = -1; - - m_selected = (wxPGProperty*) NULL; - m_pState->m_selected = (wxPGProperty*) NULL; + // This should be called after CommitChangesFromEditor(), so that + // OnValidationFailure() still has information on property's + // validation state. + OnValidationFailureReset(prevFirstSel); - // We need to always fully refresh the grid here - Refresh(false); + FreeEditors(); m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); EditorsValueWasNotModified(); @@ -3548,6 +4120,14 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY); + m_pState->DoSetSelection(p); + + // Redraw unselected + for ( unsigned int i=0; im_selected = p; m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE; - if ( p != prev ) - m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED); - - wxASSERT( m_wndEditor == (wxWindow*) NULL ); - // Do we need OnMeasureCalls? - wxSize imsz = p->OnMeasureImage(); + wxASSERT( m_wndEditor == NULL ); // // Only create editor for non-disabled non-caption @@ -3584,10 +4157,9 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) wxRect grect = GetEditorWidgetRect(p, m_selColumn); wxPoint goodPos = grect.GetPosition(); - #if wxPG_CREATE_CONTROLS_HIDDEN - int coord_adjust = m_height - goodPos.y; - goodPos.y += coord_adjust; - #endif + + // Editor appearance can now be considered clear + m_editorAppearance.SetEmptyData(); const wxPGEditor* editor = p->GetEditorClass(); wxCHECK_MSG(editor, false, @@ -3595,28 +4167,31 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_iFlags &= ~wxPG_FL_FIXED_WIDTH_EDITOR; - wxPGWindowList wndList = editor->CreateControls(this, - p, - goodPos, - grect.GetSize()); + wxPGWindowList wndList = + editor->CreateControls(this, + p, + goodPos, + grect.GetSize()); m_wndEditor = wndList.m_primary; m_wndEditor2 = wndList.m_secondary; - wxWindow* primaryCtrl = GetEditorControl(); + primaryCtrl = GetEditorControl(); // // Essentially, primaryCtrl == m_wndEditor // - // NOTE: It is allowed for m_wndEditor to be NULL - in this case - // value is drawn as normal, and m_wndEditor2 is assumed - // to be a right-aligned button that triggers a separate editorCtrl - // window. + // NOTE: It is allowed for m_wndEditor to be NULL - in this + // case value is drawn as normal, and m_wndEditor2 is + // assumed to be a right-aligned button that triggers + // a separate editorCtrl window. if ( m_wndEditor ) { - wxASSERT_MSG( m_wndEditor->GetParent() == canvas, - wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") ); + wxASSERT_MSG( m_wndEditor->GetParent() == GetPanel(), + "CreateControls must use result of " + "wxPropertyGrid::GetPanel() as parent " + "of controls." ); // Set validator, if any #if wxUSE_VALIDATORS @@ -3630,21 +4205,10 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) // If it has modified status, use bold font // (must be done before capturing m_ctrlXAdjust) - if ( (p->m_flags & wxPG_PROP_MODIFIED) && (m_windowStyle & wxPG_BOLD_MODIFIED) ) + if ( (p->m_flags & wxPG_PROP_MODIFIED) && + (m_windowStyle & wxPG_BOLD_MODIFIED) ) SetCurControlBoldFont(); - // - // Fix TextCtrl indentation - #if defined(__WXMSW__) && !defined(__WXWINCE__) - wxTextCtrl* tc = NULL; - if ( primaryCtrl->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) ) - tc = ((wxOwnerDrawnComboBox*)primaryCtrl)->GetTextCtrl(); - else - tc = wxDynamicCast(primaryCtrl, wxTextCtrl); - if ( tc ) - ::SendMessage(GetHwndOf(tc), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0)); - #endif - // Store x relative to splitter (we'll need it). m_ctrlXAdjust = m_wndEditor->GetPosition().x - splitterX; @@ -3657,15 +4221,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_wndEditor->SetSizeHints(3, 3); - #if wxPG_CREATE_CONTROLS_HIDDEN - m_wndEditor->Show(false); - m_wndEditor->Freeze(); - - goodPos = m_wndEditor->GetPosition(); - goodPos.y -= coord_adjust; - m_wndEditor->Move( goodPos ); - #endif - SetupChildEventHandling(primaryCtrl); // Focus and select all (wxTextCtrl, wxComboBox etc) @@ -3675,12 +4230,20 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) p->GetEditorClass()->OnFocus(p, primaryCtrl); } + else + { + if ( p->IsValueUnspecified() ) + SetEditorAppearance(m_unspecifiedAppearance, + true); + } } if ( m_wndEditor2 ) { - wxASSERT_MSG( m_wndEditor2->GetParent() == canvas, - wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") ); + wxASSERT_MSG( m_wndEditor2->GetParent() == GetPanel(), + "CreateControls must use result of " + "wxPropertyGrid::GetPanel() as parent " + "of controls." ); // Get proper id for wndSecondary m_wndSecId = m_wndEditor2->GetId(); @@ -3691,19 +4254,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_wndEditor2->SetSizeHints(3,3); - #if wxPG_CREATE_CONTROLS_HIDDEN - wxRect sec_rect = m_wndEditor2->GetRect(); - sec_rect.y -= coord_adjust; - - // Fine tuning required to fix "oversized" - // button disappearance bug. - if ( sec_rect.y < 0 ) - { - sec_rect.height += sec_rect.y; - sec_rect.y = 0; - } - m_wndEditor2->SetSize( sec_rect ); - #endif m_wndEditor2->Show(); SetupChildEventHandling(m_wndEditor2); @@ -3724,7 +4274,8 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } else { - // Make sure focus is in grid canvas (important for wxGTK, at least) + // Make sure focus is in grid canvas (important for wxGTK, + // at least) SetFocusOnCanvas(); } @@ -3737,13 +4288,11 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) if ( m_wndEditor ) { - #if wxPG_CREATE_CONTROLS_HIDDEN - m_wndEditor->Thaw(); - #endif m_wndEditor->Show(true); } - DrawItems(p, p); + if ( !(flags & wxPG_SEL_NO_REFRESH) ) + DrawItem(p); } else { @@ -3754,41 +4303,30 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY); } -#if wxUSE_STATUSBAR + const wxString* pHelpString = NULL; - // - // Show help text in status bar. - // (if found and grid not embedded in manager with help box and - // style wxPG_EX_HELP_AS_TOOLTIPS is not used). - // + if ( p ) + pHelpString = &p->GetHelpString(); if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) ) { - wxStatusBar* statusbar = (wxStatusBar*) NULL; - if ( !(m_iFlags & wxPG_FL_NOSTATUSBARHELP) ) - { - wxFrame* frame = wxDynamicCast(::wxGetTopLevelParent(this),wxFrame); - if ( frame ) - statusbar = frame->GetStatusBar(); - } +#if wxUSE_STATUSBAR + // + // Show help text in status bar. + // (if found and grid not embedded in manager with help box and + // style wxPG_EX_HELP_AS_TOOLTIPS is not used). + // + wxStatusBar* statusbar = GetStatusBar(); if ( statusbar ) { - const wxString* pHelpString = (const wxString*) NULL; - - if ( p ) + if ( pHelpString && !pHelpString->empty() ) { - pHelpString = &p->GetHelpString(); - if ( pHelpString->length() ) - { - // Set help box text. - statusbar->SetStatusText( *pHelpString ); - m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR; - } + // Set help box text. + statusbar->SetStatusText( *pHelpString ); + m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR; } - - if ( (!pHelpString || !pHelpString->length()) && - (m_iFlags & wxPG_FL_STRING_IN_STATUSBAR) ) + else if ( m_iFlags & wxPG_FL_STRING_IN_STATUSBAR ) { // Clear help box - but only if it was written // by us at previous time. @@ -3796,13 +4334,25 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR); } } +#endif } + else + { +#if wxPG_SUPPORT_TOOLTIPS + // + // Show help as a tool tip on the editor control. + // + if ( pHelpString && !pHelpString->empty() && + primaryCtrl ) + { + primaryCtrl->SetToolTip(*pHelpString); + } #endif - - m_inDoSelectProperty = 0; + } // call wx event handler (here so that it also occurs on deselection) - SendEvent( wxEVT_PG_SELECTED, m_selected, NULL, flags ); + if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) ) + SendEvent( wxEVT_PG_SELECTED, p, NULL ); return true; } @@ -3811,25 +4361,61 @@ 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; } // ----------------------------------------------------------------------- -// This method is not inline because it called dozens of times -// (i.e. two-arg function calls create smaller code size). -bool wxPropertyGrid::DoClearSelection() +void wxPropertyGrid::RefreshEditor() +{ + wxPGProperty* p = GetSelection(); + if ( !p ) + return; + + wxWindow* wnd = GetEditorControl(); + if ( !wnd ) + return; + + // Set editor font boldness - must do this before + // calling UpdateControl(). + if ( HasFlag(wxPG_BOLD_MODIFIED) ) + { + if ( p->HasFlag(wxPG_PROP_MODIFIED) ) + wnd->SetFont(GetCaptionFont()); + else + wnd->SetFont(GetFont()); + } + + const wxPGEditor* editorClass = p->GetEditorClass(); + + editorClass->UpdateControl(p, wnd); + + if ( p->IsValueUnspecified() ) + SetEditorAppearance(m_unspecifiedAppearance, true); +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::SelectProperty( wxPGPropArg id, bool focus ) { - return DoSelectProperty((wxPGProperty*)NULL); + wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) + + int flags = wxPG_SEL_DONT_SEND_EVENT; + if ( focus ) + flags |= wxPG_SEL_FOCUS; + + return DoSelectProperty(p, flags); } // ----------------------------------------------------------------------- @@ -3839,17 +4425,17 @@ 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) ) { - if ( !ClearSelection() ) - return false; + DoClearSelection(); } // Store dont-center-splitter flag 'cause we need to temporarily set it - wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER; - m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER; + bool prevDontCenterSplitter = m_pState->m_dontCenterSplitter; + m_pState->m_dontCenterSplitter = true; bool res = m_pState->DoCollapse(pwc); @@ -3859,20 +4445,10 @@ bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents ) SendEvent( wxEVT_PG_ITEM_COLLAPSED, p ); RecalculateVirtualSize(); - - // Redraw etc. only if collapsed was visible. - if (pwc->IsVisible() && - !m_frozen && - ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) ) - { - // When item is collapsed so that scrollbar would move, - // graphics mess is about (unless we redraw everything). - Refresh(); - } + Refresh(); } - // Clear dont-center-splitter flag if it wasn't set - m_iFlags = (m_iFlags & ~wxPG_FL_DONT_CENTER_SPLITTER) | old_flag; + m_pState->m_dontCenterSplitter = prevDontCenterSplitter; return res; } @@ -3886,8 +4462,8 @@ bool wxPropertyGrid::DoExpand( wxPGProperty* p, bool sendEvents ) wxPGProperty* pwc = (wxPGProperty*)p; // Store dont-center-splitter flag 'cause we need to temporarily set it - wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER; - m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER; + bool prevDontCenterSplitter = m_pState->m_dontCenterSplitter; + m_pState->m_dontCenterSplitter = true; bool res = m_pState->DoExpand(pwc); @@ -3897,23 +4473,10 @@ bool wxPropertyGrid::DoExpand( wxPGProperty* p, bool sendEvents ) SendEvent( wxEVT_PG_ITEM_EXPANDED, p ); RecalculateVirtualSize(); - - // Redraw etc. only if expanded was visible. - if ( pwc->IsVisible() && !m_frozen && - ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) - ) - { - // Redraw - #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT - Refresh(); - #else - DrawItems(pwc, NULL); - #endif - } + Refresh(); } - // Clear dont-center-splitter flag if it wasn't set - m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag; + m_pState->m_dontCenterSplitter = prevDontCenterSplitter; return res; } @@ -3925,13 +4488,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) ) { - if ( !ClearSelection() ) + if ( !DoRemoveFromSelection(p, flags) ) return false; + selRemoveCount += 1; } + } m_pState->DoHideProperty(p, hide, flags); @@ -3948,7 +4516,11 @@ bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags ) void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) { - if ( (m_iFlags & wxPG_FL_RECALCULATING_VIRTUAL_SIZE) || m_frozen ) + // Don't check for !HasInternalFlag(wxPG_FL_INITIALIZED) here. Otherwise + // virtual size calculation may go wrong. + if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE) || + m_frozen || + !m_pState ) return; // @@ -3958,17 +4530,10 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) m_pState->EnsureVirtualHeight(); -#ifdef __WXDEBUG__ - int by1 = m_pState->GetVirtualHeight(); - int by2 = m_pState->GetActualVirtualHeight(); - if ( by1 != by2 ) - { - wxString s = wxString::Format(wxT("VirtualHeight=%i, ActualVirtualHeight=%i, should match!"), by1, by2); - wxASSERT_MSG( false, - s.c_str() ); - wxLogDebug(s); - } -#endif + wxASSERT_LEVEL_2_MSG( + m_pState->GetVirtualHeight() == m_pState->GetActualVirtualHeight(), + "VirtualHeight and ActualVirtualHeight should match" + ); m_iFlags |= wxPG_FL_RECALCULATING_VIRTUAL_SIZE; @@ -3979,7 +4544,7 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) GetClientSize(&width,&height); // Now adjust virtual size. - SetVirtualSize(x, y); + SetVirtualSize(x, y); int xAmount = 0; int xPos = 0; @@ -3998,12 +4563,17 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) else if ( xPos > (xAmount-(width/wxPG_PIXELS_PER_UNIT)) ) xPos = 0; - int yAmount = (y+wxPG_PIXELS_PER_UNIT+2)/wxPG_PIXELS_PER_UNIT; + int yAmount = y / wxPG_PIXELS_PER_UNIT; int yPos = GetScrollPos( wxVERTICAL ); SetScrollbars( wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT, xAmount, yAmount, xPos, yPos, true ); + // This may be needed in addition to calling SetScrollbars() + // when class inherits from wxScrollHelper instead of + // actual wxScrolled. + AdjustScrollbars(); + // Must re-get size now GetClientSize(&width,&height); @@ -4016,11 +4586,9 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) m_width = width; m_height = height; - m_canvas->SetSize( x, y ); - m_pState->CheckColumnWidths(); - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE; @@ -4034,12 +4602,11 @@ void wxPropertyGrid::OnResize( wxSizeEvent& event ) return; int width, height; - GetClientSize(&width,&height); + GetClientSize(&width, &height); m_width = width; m_height = height; -#if wxPG_DOUBLE_BUFFER if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) ) { int dblh = (m_lineHeight*2); @@ -4067,8 +4634,6 @@ void wxPropertyGrid::OnResize( wxSizeEvent& event ) } } -#endif - m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true ); m_ncWidth = event.GetSize().x; @@ -4104,7 +4669,24 @@ void wxPropertyGrid::SetVirtualWidth( int width ) void wxPropertyGrid::SetFocusOnCanvas() { - m_canvas->SetFocusIgnoringChildren(); + // To prevent wxPropertyGrid from stealing focus from other controls, + // only move focus to the grid if it was already in one if its child + // controls. + wxWindow* focus = wxWindow::FindFocus(); + if ( focus ) + { + wxWindow* parent = focus->GetParent(); + while ( parent ) + { + if ( parent == this ) + { + SetFocus(); + break; + } + parent = parent->GetParent(); + } + } + m_editorFocused = 0; } @@ -4114,22 +4696,40 @@ 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 ) { + // selFlags should have wxPG_SEL_NOVALIDATE if event is not + // vetoable. + // 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); - if ( pValue ) + evt.SetColumn(column); + if ( eventType == wxEVT_PG_CHANGING ) { + wxASSERT( pValue ); evt.SetCanVeto(true); - evt.SetupValidationInfo(); m_validationInfo.m_pValue = pValue; + evt.SetupValidationInfo(); + } + else + { + if ( p ) + evt.SetPropertyValue(p->GetValue()); + + if ( !(selFlags & wxPG_SEL_NOVALIDATE) ) + evt.SetCanVeto(true); } - wxEvtHandler* evtHandler = m_eventObject->GetEventHandler(); - evtHandler->ProcessEvent(evt); + wxPropertyGridEvent* prevProcessedEvent = m_processedEvent; + m_processedEvent = &evt; + m_eventObject->HandleWindowEvent(evt); + m_processedEvent = prevProcessedEvent; return evt.WasVetoed(); } @@ -4179,7 +4779,9 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even ) ) { - if ( !DoSelectProperty( p ) ) + if ( !AddToSelectionFromInputEvent( p, + columnHit, + &event ) ) return res; // On double-click, expand/collapse. @@ -4199,7 +4801,10 @@ 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, + columnHit, + &event, + selFlag ) ) return res; m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK); @@ -4213,7 +4818,10 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even else DoExpand( p, true ); } - res = false; + // Do not Skip() the event after selection has been made. + // Otherwise default event handling behaviour kicks in + // and may revert focus back to the main canvas. + res = true; } else { @@ -4223,42 +4831,59 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even if ( event.GetEventType() == wxEVT_LEFT_DCLICK ) { // Double-clicking the splitter causes auto-centering - CenterSplitter( true ); + if ( m_pState->GetColumnCount() <= 2 ) + { + ResetColumnSizes( true ); + + SendEvent(wxEVT_PG_COL_DRAGGING, + m_propHover, + NULL, + wxPG_SEL_NOVALIDATE, + (unsigned int)m_draggedSplitter); + } } else if ( m_dragStatus == 0 ) { - // - // Begin draggin the splitter - // - if ( m_wndEditor ) - { - // Changes must be committed here or the - // value won't be drawn correctly - if ( !CommitChangesFromEditor() ) - return res; + // + // Begin draggin the splitter + // - m_wndEditor->Show ( false ); - } + // send event + DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); - if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) ) + // Allow application to veto dragging + if ( !SendEvent(wxEVT_PG_COL_BEGIN_DRAG, + p, NULL, 0, + (unsigned int)splitterHit) ) { - m_canvas->CaptureMouse(); - m_iFlags |= wxPG_FL_MOUSE_CAPTURED; - } + if ( m_wndEditor ) + { + // Changes must be committed here or the + // value won't be drawn correctly + if ( !CommitChangesFromEditor() ) + return res; + + m_wndEditor->Show ( false ); + } - m_dragStatus = 1; - m_draggedSplitter = splitterHit; - m_dragOffset = splitterHitOffset; + if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) ) + { + CaptureMouse(); + m_iFlags |= wxPG_FL_MOUSE_CAPTURED; + } - wxClientDC dc(m_canvas); + m_dragStatus = 1; + m_draggedSplitter = splitterHit; + m_dragOffset = splitterHitOffset; - #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT - // Fixes button disappearance bug - if ( m_wndEditor2 ) - m_wndEditor2->Show ( false ); - #endif + #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT + // Fixes button disappearance bug + if ( m_wndEditor2 ) + m_wndEditor2->Show ( false ); + #endif - m_startingSplitterX = x - splitterHitOffset; + m_startingSplitterX = x - splitterHitOffset; + } } } } @@ -4270,6 +4895,10 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even { int nx = x + m_marginWidth - marginEnds; // Normalize x. + // Fine tune cell button x + if ( !p->IsCategory() ) + nx -= IN_CELL_EXPANDER_BUTTON_X_ADJUST; + if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) ) { int y2 = y % m_lineHeight; @@ -4290,15 +4919,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, m_colHover, &event); // Send right click event. SendEvent( wxEVT_PG_RIGHT_CLICK, p ); @@ -4310,16 +4939,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, m_colHover, &event); // Send double-click event. SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover ); @@ -4331,36 +4960,15 @@ bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), unsigned int WXUNU // ----------------------------------------------------------------------- -#if wxPG_SUPPORT_TOOLTIPS - -void wxPropertyGrid::SetToolTip( const wxString& tipString ) -{ - if ( tipString.length() ) - { - m_canvas->SetToolTip(tipString); - } - else - { - #if wxPG_ALLOW_EMPTY_TOOLTIPS - m_canvas->SetToolTip( m_emptyString ); - #else - m_canvas->SetToolTip( NULL ); - #endif - } -} - -#endif // #if wxPG_SUPPORT_TOOLTIPS - -// ----------------------------------------------------------------------- - // Return false if should be skipped -bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event ) +bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, + wxMouseEvent &event ) { // Safety check (needed because mouse capturing may // otherwise freeze the control) if ( m_dragStatus > 0 && !event.Dragging() ) { - HandleMouseUp(x,y,event); + HandleMouseUp(x, y, event); } wxPropertyGridPageState* state = m_pState; @@ -4369,6 +4977,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) && @@ -4376,21 +4986,21 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event { int newSplitterX = x - m_dragOffset; - int splitterX = x - splitterHitOffset; // Splitter redraw required? if ( newSplitterX != splitterX ) { // Move everything - SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER); - state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false ); - state->m_fSplitterX = (float) newSplitterX; - - if ( m_selected ) - CorrectEditorWidgetSizeX(); - - Update(); - Refresh(); + DoSetSplitterPosition(newSplitterX, + m_draggedSplitter, + wxPG_SPLITTER_REFRESH | + wxPG_SPLITTER_FROM_EVENT); + + SendEvent(wxEVT_PG_COL_DRAGGING, + m_propHover, + NULL, + wxPG_SEL_NOVALIDATE, + (unsigned int)m_draggedSplitter); } m_dragStatus = 2; @@ -4411,9 +5021,9 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event int curPropHoverY = y - (y % ih); // On which item it hovers - if ( ( !m_propHover ) + if ( !m_propHover || - ( m_propHover && ( sy < m_propHoverY || sy >= (m_propHoverY+ih) ) ) + ( sy < m_propHoverY || sy >= (m_propHoverY+ih) ) ) { // Mouse moves on another property @@ -4439,8 +5049,6 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event // if ( m_windowStyle & wxPG_TOOLTIPS ) { - wxToolTip* tooltip = m_canvas->GetToolTip(); - if ( m_propHover != prevHover || prevSide != m_mouseSide ) { if ( m_propHover && !m_propHover->IsCategory() ) @@ -4470,42 +5078,28 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event space = m_width - splitterX; if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE ) - space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2; + space -= wxPG_CUSTOM_IMAGE_WIDTH + + wxCC_CUSTOM_IMAGE_MARGIN1 + + wxCC_CUSTOM_IMAGE_MARGIN2; } if ( space ) { int tw, th; - GetTextExtent( tipString, &tw, &th, 0, 0, &m_font ); + GetTextExtent( tipString, &tw, &th, 0, 0 ); if ( tw > space ) - { SetToolTip( tipString ); - } } else { - if ( tooltip ) - { - #if wxPG_ALLOW_EMPTY_TOOLTIPS - m_canvas->SetToolTip( m_emptyString ); - #else - m_canvas->SetToolTip( NULL ); - #endif - } + SetToolTip( m_emptyString ); } } } else { - if ( tooltip ) - { - #if wxPG_ALLOW_EMPTY_TOOLTIPS - m_canvas->SetToolTip( m_emptyString ); - #else - m_canvas->SetToolTip( NULL ); - #endif - } + SetToolTip( m_emptyString ); } } } @@ -4525,15 +5119,14 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event // (also not if we were dragging and its started // outside the splitter region) - if ( m_propHover && - !m_propHover->IsCategory() && + if ( !m_propHover->IsCategory() && !event.Dragging() ) { // hovering on splitter - // NB: Condition disabled since MouseLeave event (from the editor control) cannot be - // reliably detected. + // NB: Condition disabled since MouseLeave event (from the + // editor control) cannot be reliably detected. //if ( m_curcursor != wxCURSOR_SIZEWE ) CustomSetCursor( wxCURSOR_SIZEWE, true ); @@ -4546,6 +5139,38 @@ 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() && + columnHit != 1 && + !state->DoIsPropertySelected(m_propHover) ) + { + // Additional requirement is that the hovered property + // is adjacent to edges of selection. + const wxArrayPGProperty& selection = GetSelectedProperties(); + + // Since categories cannot be selected along with 'other' + // properties, exclude them from iterator flags. + int iterFlags = wxPG_ITERATE_VISIBLE & (~wxPG_PROP_CATEGORY); + + for ( int i=(selection.size()-1); i>=0; i-- ) + { + // TODO: This could be optimized by keeping track of + // which properties are at the edges of selection. + wxPGProperty* selProp = selection[i]; + if ( state->ArePropertiesAdjacent(m_propHover, selProp, + iterFlags) ) + { + DoAddToSelection(m_propHover); + break; + } + } + } } return true; } @@ -4575,13 +5200,25 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y), // (it is only here as a reminder to not to do it) //splitterX = x; - // Disable splitter auto-centering - m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER; + SendEvent(wxEVT_PG_COL_END_DRAG, + m_propHover, + NULL, + wxPG_SEL_NOVALIDATE, + (unsigned int)m_draggedSplitter); + + // Disable splitter auto-centering (but only if moved any - + // otherwise we end up disabling auto-center even after a + // recentering double-click). + int posDiff = abs(m_startingSplitterX - + GetSplitterPosition(m_draggedSplitter)); + + if ( posDiff > 1 ) + state->m_dontCenterSplitter = true; // This is necessary to return cursor if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) { - m_canvas->ReleaseMouse(); + ReleaseMouse(); m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED); } @@ -4595,8 +5232,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 ) { @@ -4622,10 +5260,8 @@ bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py ) { int splitterX = GetSplitterPosition(); - //int ux, uy; - //CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy ); - int ux = event.m_x; - int uy = event.m_y; + int ux, uy; + CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy ); wxWindow* wnd = GetEditorControl(); @@ -4639,7 +5275,7 @@ bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py ) wxRect r; if ( wnd ) r = wnd->GetRect(); - if ( wnd == (wxWindow*) NULL || m_dragStatus || + if ( wnd == NULL || m_dragStatus || ( ux <= (splitterX + wxPG_SPLITTERX_DETECTMARGIN2) || ux >= (r.x+r.width) || @@ -4666,9 +5302,13 @@ void wxPropertyGrid::OnMouseClick( wxMouseEvent &event ) int x, y; if ( OnMouseCommon( event, &x, &y ) ) { - HandleMouseClick(x,y,event); + if ( !HandleMouseClick(x, y, event) ) + event.Skip(); + } + else + { + event.Skip(); } - event.Skip(); } // ----------------------------------------------------------------------- @@ -4691,7 +5331,9 @@ void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event ) int x, y; CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y ); HandleMouseDoubleClick(x,y,event); - event.Skip(); + + // Do not Skip() event here - OnMouseClick() call above + // should have already taken care of it. } // ----------------------------------------------------------------------- @@ -4708,22 +5350,18 @@ void wxPropertyGrid::OnMouseMove( wxMouseEvent &event ) // ----------------------------------------------------------------------- -void wxPropertyGrid::OnMouseMoveBottom( wxMouseEvent& WXUNUSED(event) ) -{ - // Called when mouse moves in the empty space below the properties. - CustomSetCursor( wxCURSOR_ARROW ); -} - -// ----------------------------------------------------------------------- - void wxPropertyGrid::OnMouseUp( wxMouseEvent &event ) { int x, y; if ( OnMouseCommon( event, &x, &y ) ) { - HandleMouseUp(x,y,event); + if ( !HandleMouseUp(x, y, event) ) + event.Skip(); + } + else + { + event.Skip(); } - event.Skip(); } // ----------------------------------------------------------------------- @@ -4750,7 +5388,7 @@ void wxPropertyGrid::OnMouseEntry( wxMouseEvent &event ) else if ( event.Leaving() ) { // Without this, wxSpinCtrl editor will sometimes have wrong cursor - m_canvas->SetCursor( wxNullCursor ); + SetCursor( wxNullCursor ); // Get real cursor position wxPoint pt = ScreenToClient(::wxGetMousePosition()); @@ -4820,8 +5458,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(); } @@ -4900,44 +5540,89 @@ void wxPropertyGrid::AddActionTrigger( int action, int keycode, int modifiers ) void wxPropertyGrid::ClearActionTriggers( int action ) { wxPGHashMapI2I::iterator it; + bool didSomething; - for ( it = m_actionTriggers.begin(); it != m_actionTriggers.end(); it++ ) + do { - if ( it->second == action ) + didSomething = false; + + for ( it = m_actionTriggers.begin(); + it != m_actionTriggers.end(); + it++ ) { - m_actionTriggers.erase(it); + if ( it->second == action ) + { + m_actionTriggers.erase(it); + didSomething = true; + break; + } } } + while ( didSomething ); } -static void CopyTextToClipboard( const wxString& text ) -{ - if ( wxTheClipboard->Open() ) - { - // This data objects are held by the clipboard, - // so do not delete them in the app. - wxTheClipboard->SetData( new wxTextDataObject(text) ); - wxTheClipboard->Close(); - } -} - -void wxPropertyGrid::HandleKeyEvent(wxKeyEvent &event) +void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) { // // Handles key event when editor control is not focused. // - wxASSERT( !m_frozen ); - if ( m_frozen ) - return; + wxCHECK2(!m_frozen, return); // Travelsal between items, collapsing/expanding, etc. + wxPGProperty* selected = GetSelection(); int keycode = event.GetKeyCode(); + bool editorFocused = IsEditorFocused(); if ( keycode == WXK_TAB ) { - if (m_selected) - DoSelectProperty( m_selected, wxPG_SEL_FOCUS ); + wxWindow* mainControl; + + if ( HasInternalFlag(wxPG_FL_IN_MANAGER) ) + mainControl = GetParent(); + else + mainControl = this; + + if ( !event.ShiftDown() ) + { + if ( !editorFocused && m_wndEditor ) + { + DoSelectProperty( selected, wxPG_SEL_FOCUS ); + } + else + { + // Tab traversal workaround for platforms on which + // wxWindow::Navigate() may navigate into first child + // instead of next sibling. Does not work perfectly + // in every scenario (for instance, when property grid + // is either first or last control). + #if defined(__WXGTK__) + wxWindow* sibling = mainControl->GetNextSibling(); + if ( sibling ) + sibling->SetFocusFromKbd(); + #else + Navigate(wxNavigationKeyEvent::IsForward); + #endif + } + } + else + { + if ( editorFocused ) + { + UnfocusEditor(); + } + else + { + #if defined(__WXGTK__) + wxWindow* sibling = mainControl->GetPrevSibling(); + if ( sibling ) + sibling->SetFocusFromKbd(); + #else + Navigate(wxNavigationKeyEvent::IsBackward); + #endif + } + } + return; } @@ -4952,189 +5637,149 @@ void wxPropertyGrid::HandleKeyEvent(wxKeyEvent &event) int secondAction; int action = KeyEventToActions(event, &secondAction); - if ( m_selected ) + if ( editorFocused && action == wxPG_ACTION_CANCEL_EDIT ) { - - // Show dialog? - if ( ButtonTriggerKeyTest(action, event) ) - return; - - wxPGProperty* p = m_selected; - - if ( action == wxPG_ACTION_COPY ) - { - CopyTextToClipboard(p->GetDisplayedString()); - } - else + // + // Esc cancels any changes + if ( IsEditorsValueModified() ) { - // Travel and expand/collapse - int selectDir = -2; - - if ( p->GetChildCount() && - !(p->m_flags & wxPG_PROP_DISABLED) - ) - { - if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY ) - { - if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse(p) ) - keycode = 0; - } - else if ( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY ) - { - if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand(p) ) - keycode = 0; - } - } + EditorsValueWasNotModified(); - if ( keycode ) - { - if ( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY ) - { - selectDir = -1; - } - else if ( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY ) - { - selectDir = 1; - } - else - { - event.Skip(); - } + // Update the control as well + selected->GetEditorClass()-> + SetControlStringValue( selected, + GetEditorControl(), + selected->GetDisplayedString() ); + } - } + OnValidationFailureReset(selected); - if ( selectDir >= -1 ) - { - p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir ); - if ( p ) - DoSelectProperty(p); - } - } + UnfocusEditor(); + return; } - else + + // Except for TAB, ESC, and any keys specifically dedicated to + // wxPropertyGrid itself, handle child control events in child control. + if ( fromChild && + wxPGFindInVector(m_dedicatedKeys, keycode) == wxNOT_FOUND ) { - // If nothing was selected, select the first item now - // (or navigate out of tab). - if ( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT ) + // Only propagate event if it had modifiers + if ( !event.HasModifiers() ) { - wxPGProperty* p = wxPropertyGridInterface::GetFirst(); - if ( p ) DoSelectProperty(p); + event.StopPropagation(); } + event.Skip(); + return; } -} - -// ----------------------------------------------------------------------- -// Potentially handles a keyboard event for editor controls. -// Returns false if event should *not* be skipped (on true it can -// be optionally skipped). -// Basicly, false means that SelectProperty was called (or was about -// to be called, if canDestroy was false). -bool wxPropertyGrid::HandleChildKey( wxKeyEvent& event ) -{ - bool res = true; + bool wasHandled = false; - if ( !m_selected || !m_wndEditor ) + if ( selected ) { - return true; - } + // Show dialog? + if ( ButtonTriggerKeyTest(action, event) ) + return; - int action = KeyEventToAction(event); + wxPGProperty* p = selected; - // Unfocus? - if ( action == wxPG_ACTION_CANCEL_EDIT ) - { - // - // Esc cancels any changes - if ( IsEditorsValueModified() ) + if ( action == wxPG_ACTION_EDIT && !editorFocused ) { - EditorsValueWasNotModified(); - - // Update the control as well - m_selected->GetEditorClass()->SetControlStringValue( m_selected, - GetEditorControl(), - m_selected->GetDisplayedString() ); + DoSelectProperty( p, wxPG_SEL_FOCUS ); + wasHandled = true; } - OnValidationFailureReset(m_selected); + // Travel and expand/collapse + int selectDir = -2; - res = false; - UnfocusEditor(); - } - else if ( action == wxPG_ACTION_COPY ) - { - // NB: There is some problem with getting native cut-copy-paste keys to go through - // for embedded editor wxTextCtrl. This is why we emulate. - // - wxTextCtrl* tc = GetEditorTextCtrl(); - if ( tc ) + if ( p->GetChildCount() ) { - wxString sel = tc->GetStringSelection(); - if ( sel.length() ) - CopyTextToClipboard(sel); + if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY ) + { + if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse(p) ) + wasHandled = true; + } + else if ( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY ) + { + if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand(p) ) + wasHandled = true; + } } - else + + if ( !wasHandled ) { - CopyTextToClipboard(m_selected->GetDisplayedString()); + if ( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY ) + { + selectDir = -1; + } + else if ( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY ) + { + selectDir = 1; + } } - } - else if ( action == wxPG_ACTION_CUT ) - { - wxTextCtrl* tc = GetEditorTextCtrl(); - if ( tc ) + + if ( selectDir >= -1 ) { - long from, to; - tc->GetSelection(&from, &to); - if ( from < to ) + p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir ); + if ( p ) { - CopyTextToClipboard(tc->GetStringSelection()); - tc->Remove(from, to); + int selFlags = 0; + int reopenLabelEditorCol = -1; + + if ( editorFocused ) + { + // If editor was focused, then make the next editor + // focused as well + selFlags |= wxPG_SEL_FOCUS; + } + else + { + // Also maintain the same label editor focus state + if ( m_labelEditor ) + reopenLabelEditorCol = m_selColumn; + } + + DoSelectProperty(p, selFlags); + + if ( reopenLabelEditorCol >= 0 ) + DoBeginLabelEdit(reopenLabelEditorCol); } + wasHandled = true; } } - else if ( action == wxPG_ACTION_PASTE ) + else { - wxTextCtrl* tc = GetEditorTextCtrl(); - if ( tc ) + // If nothing was selected, select the first item now + // (or navigate out of tab). + if ( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT ) { - if (wxTheClipboard->Open()) - { - if (wxTheClipboard->IsSupported( wxDF_TEXT )) - { - wxTextDataObject data; - wxTheClipboard->GetData( data ); - long from, to; - tc->GetSelection(&from, &to); - if ( from < to ) - { - tc->Remove(from, to); - tc->WriteText(data.GetText()); - } - else - { - tc->WriteText(data.GetText()); - } - } - wxTheClipboard->Close(); - } + wxPGProperty* p = wxPropertyGridInterface::GetFirst(); + if ( p ) DoSelectProperty(p); + wasHandled = true; } } - return res; + if ( !wasHandled ) + event.Skip(); } // ----------------------------------------------------------------------- void wxPropertyGrid::OnKey( wxKeyEvent &event ) { - HandleKeyEvent( event ); -} - -// ----------------------------------------------------------------------- + // If there was editor open and focused, then this event should not + // really be processed here. + if ( IsEditorFocused() ) + { + // However, if event had modifiers, it is probably still best + // to skip it. + if ( event.HasModifiers() ) + event.Skip(); + else + event.StopPropagation(); + return; + } -void wxPropertyGrid::OnKeyUp(wxKeyEvent &event) -{ - event.Skip(); + HandleKeyEvent(event, false); } // ----------------------------------------------------------------------- @@ -5163,30 +5808,7 @@ bool wxPropertyGrid::ButtonTriggerKeyTest( int action, wxKeyEvent& event ) void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event ) { - int keycode = event.GetKeyCode(); - - // Ignore Alt and Control when they are down alone - if ( keycode == WXK_ALT || - keycode == WXK_CONTROL ) - { - event.Skip(); - return; - } - - if ( ButtonTriggerKeyTest(-1, event) ) - return; - - if ( HandleChildKey(event) == true ) - event.Skip(); - - GetEventHandler()->AddPendingEvent(event); -} - -void wxPropertyGrid::OnChildKeyUp( wxKeyEvent &event ) -{ - GetEventHandler()->AddPendingEvent(event); - - event.Skip(); + HandleKeyEvent(event, true); } // ----------------------------------------------------------------------- @@ -5201,12 +5823,62 @@ void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) ) if ( newFocused != m_curFocused ) HandleFocusChange( newFocused ); + + // + // Check if top-level parent has changed + if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING ) + { + wxWindow* tlp = ::wxGetTopLevelParent(this); + if ( tlp != m_tlp ) + OnTLPChanging(tlp); + } + + // + // Resolve pending property removals + if ( m_deletedProperties.size() > 0 ) + { + wxArrayPGProperty& arr = m_deletedProperties; + for ( unsigned int i=0; i 0 ) + { + wxArrayPGProperty& arr = m_removedProperties; + for ( unsigned int i=0; iGetParent(); } + // Notify editor control when it receives a focus + if ( wasEditorFocused && m_curFocused != newFocused ) + { + wxPGProperty* p = GetSelection(); + if ( p ) + { + const wxPGEditor* editor = p->GetEditorClass(); + ResetEditorAppearance(); + editor->OnFocus(p, GetEditorControl()); + } + } + m_curFocused = newFocused; if ( (m_iFlags & wxPG_FL_FOCUSED) != @@ -5267,8 +5955,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 ); } } @@ -5289,11 +5978,7 @@ void wxPropertyGrid::OnFocusEvent( wxFocusEvent& event ) void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event ) { HandleFocusChange((wxWindow*)event.GetEventObject()); - - // - // event.Skip() being commented out is aworkaround for bug reported - // in ticket #4840 (wxScrolledWindow problem with automatic scrolling). - //event.Skip(); + event.Skip(); } // ----------------------------------------------------------------------- @@ -5320,19 +6005,29 @@ void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent& WXUNUSED(event // ----------------------------------------------------------------------- // noDefCheck = true prevents infinite recursion. -wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorClass, - bool noDefCheck ) +wxPGEditor* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor* editorClass, + const wxString& editorName, + bool noDefCheck ) { wxASSERT( editorClass ); if ( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() ) RegisterDefaultEditors(); - wxString name = editorClass->GetName(); + wxString name = editorName; + if ( name.empty() ) + name = editorClass->GetName(); // Existing editor under this name? wxPGHashMapS2P::iterator vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name); + if ( vt_it != wxPGGlobalVars->m_mapEditorClasses.end() ) + { + // If this name was already used, try class name. + name = editorClass->GetClassInfo()->GetClassName(); + vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name); + } + wxCHECK_MSG( vt_it == wxPGGlobalVars->m_mapEditorClasses.end(), (wxPGEditor*) vt_it->second, "Editor with given name was already registered" ); @@ -5342,6 +6037,14 @@ wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorClass, return editorClass; } +// Use this in RegisterDefaultEditors. +#define wxPGRegisterDefaultEditorClass(EDITOR) \ + if ( wxPGEditor_##EDITOR == NULL ) \ + { \ + wxPGEditor_##EDITOR = wxPropertyGrid::RegisterEditorClass( \ + new wxPG##EDITOR##Editor, true ); \ + } + // Registers all default editor classes void wxPropertyGrid::RegisterDefaultEditors() { @@ -5407,7 +6110,7 @@ bool wxPGStringTokenizer::HasMoreTokens() } else { - i++; + ++i; m_curPos = i; return true; } @@ -5419,7 +6122,7 @@ bool wxPGStringTokenizer::HasMoreTokens() prev_a = wxT('\0'); } } - i++; + ++i; } m_curPos = str.end(); @@ -5444,19 +6147,12 @@ wxPGChoiceEntry::wxPGChoiceEntry() { } -wxPGChoiceEntry::wxPGChoiceEntry( const wxPGChoiceEntry& entry ) - : wxPGCell( entry.GetText(), entry.GetBitmap(), - entry.GetFgCol(), entry.GetBgCol() ), m_value(entry.GetValue()) -{ -} - // ----------------------------------------------------------------------- // wxPGChoicesData // ----------------------------------------------------------------------- wxPGChoicesData::wxPGChoicesData() { - m_refCount = 1; } wxPGChoicesData::~wxPGChoicesData() @@ -5466,13 +6162,6 @@ wxPGChoicesData::~wxPGChoicesData() void wxPGChoicesData::Clear() { - unsigned int i; - - for ( i=0; iGetCount(); i++ ) - m_items.push_back( new wxPGChoiceEntry(*data->Item(i)) ); -} - -// ----------------------------------------------------------------------- -// wxPGChoices -// ----------------------------------------------------------------------- - -wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, int value ) -{ - EnsureData(); - - wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value); - m_data->Insert( -1, p ); - return *p; -} - -// ----------------------------------------------------------------------- - -wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, const wxBitmap& bitmap, int value ) -{ - EnsureData(); - - wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value); - p->SetBitmap(bitmap); - m_data->Insert( -1, p ); - return *p; -} - -// ----------------------------------------------------------------------- - -wxPGChoiceEntry& wxPGChoices::Insert( const wxPGChoiceEntry& entry, int index ) -{ - EnsureData(); - - wxPGChoiceEntry* p = new wxPGChoiceEntry(entry); - m_data->Insert(index, p); - return *p; -} - -// ----------------------------------------------------------------------- - -wxPGChoiceEntry& wxPGChoices::Insert( const wxString& label, int index, int value ) -{ - EnsureData(); - - wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value); - m_data->Insert( index, p ); - return *p; -} - -// ----------------------------------------------------------------------- - -wxPGChoiceEntry& wxPGChoices::AddAsSorted( const wxString& label, int value ) -{ - EnsureData(); - - size_t index = 0; - - while ( index < GetCount() ) - { - int cmpRes = GetLabel(index).Cmp(label); - if ( cmpRes > 0 ) - break; - index++; - } - - wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value); - m_data->Insert( index, p ); - return *p; -} - -// ----------------------------------------------------------------------- - -void wxPGChoices::Add( const wxChar** labels, const ValArrItem* values ) -{ - EnsureData(); - - unsigned int itemcount = 0; - const wxChar** p = &labels[0]; - while ( *p ) { p++; itemcount++; } - - unsigned int i; - for ( i = 0; i < itemcount; i++ ) - { - int value = wxPG_INVALID_VALUE; - if ( values ) - value = values[i]; - m_data->Insert( -1, new wxPGChoiceEntry(labels[i], value) ); - } -} - -// ----------------------------------------------------------------------- - -void wxPGChoices::Add( const wxArrayString& arr, const ValArrItem* values ) -{ - EnsureData(); - - unsigned int i; - unsigned int itemcount = arr.size(); - - for ( i = 0; i < itemcount; i++ ) - { - int value = wxPG_INVALID_VALUE; - if ( values ) - value = values[i]; - m_data->Insert( -1, new wxPGChoiceEntry(arr[i], value) ); - } -} - -// ----------------------------------------------------------------------- - -void wxPGChoices::Add( const wxArrayString& arr, const wxArrayInt& arrint ) -{ - EnsureData(); - - unsigned int i; - unsigned int itemcount = arr.size(); - - for ( i = 0; i < itemcount; i++ ) - { - int value = wxPG_INVALID_VALUE; - if ( &arrint && arrint.size() ) - value = arrint[i]; - m_data->Insert( -1, new wxPGChoiceEntry(arr[i], value) ); - } -} - -// ----------------------------------------------------------------------- - -void wxPGChoices::RemoveAt(size_t nIndex, size_t count) -{ - wxASSERT( m_data->m_refCount != 0xFFFFFFF ); - unsigned int i; - for ( i=nIndex; i<(nIndex+count); i++) - delete m_data->Item(i); - m_data->m_items.erase(m_data->m_items.begin()+nIndex, - m_data->m_items.begin()+nIndex+count); -} - -// ----------------------------------------------------------------------- - -int wxPGChoices::Index( const wxString& str ) const -{ - if ( IsOk() ) - { - unsigned int i; - for ( i=0; i< m_data->GetCount(); i++ ) - { - if ( m_data->Item(i)->GetText() == str ) - return i; - } - } - return -1; -} - -// ----------------------------------------------------------------------- - -int wxPGChoices::Index( int val ) const -{ - if ( IsOk() ) - { - unsigned int i; - for ( i=0; i< m_data->GetCount(); i++ ) - { - if ( m_data->Item(i)->GetValue() == val ) - return i; - } - } - return -1; -} - -// ----------------------------------------------------------------------- - -wxArrayString wxPGChoices::GetLabels() const -{ - wxArrayString arr; - unsigned int i; - - if ( this && IsOk() ) - for ( i=0; i= 0 ) - arr.Add(GetValue(index)); - else - arr.Add(wxPG_INVALID_VALUE); - } - } - - return arr; + m_items = data->m_items; } -// ----------------------------------------------------------------------- - -wxArrayInt wxPGChoices::GetIndicesForStrings( const wxArrayString& strings, - wxArrayString* unmatched ) const +wxPGChoiceEntry& wxPGChoicesData::Insert( int index, + const wxPGChoiceEntry& item ) { - wxArrayInt arr; - - if ( IsOk() ) + wxVector::iterator it; + if ( index == -1 ) { - unsigned int i; - for ( i=0; i< strings.size(); i++ ) - { - const wxString& str = strings[i]; - int index = Index(str); - if ( index >= 0 ) - arr.Add(index); - else if ( unmatched ) - unmatched->Add(str); - } + it = m_items.end(); + index = (int) m_items.size(); } - - return arr; -} - -// ----------------------------------------------------------------------- - -void wxPGChoices::AssignData( wxPGChoicesData* data ) -{ - Free(); - - if ( data != wxPGChoicesEmptyData ) + else { - m_data = data; - data->m_refCount++; + it = m_items.begin() + index; } -} -// ----------------------------------------------------------------------- + m_items.insert(it, item); -void wxPGChoices::Init() -{ - m_data = wxPGChoicesEmptyData; -} + wxPGChoiceEntry& ownEntry = m_items[index]; -// ----------------------------------------------------------------------- + // Need to fix value? + if ( ownEntry.GetValue() == wxPG_INVALID_VALUE ) + ownEntry.SetValue(index); -void wxPGChoices::Free() -{ - if ( m_data != wxPGChoicesEmptyData ) - { - m_data->DecRef(); - m_data = wxPGChoicesEmptyData; - } + return ownEntry; } // ----------------------------------------------------------------------- @@ -5759,22 +6204,27 @@ void wxPGChoices::Free() IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent, wxCommandEvent) -DEFINE_EVENT_TYPE( wxEVT_PG_SELECTED ) -DEFINE_EVENT_TYPE( wxEVT_PG_CHANGING ) -DEFINE_EVENT_TYPE( wxEVT_PG_CHANGED ) -DEFINE_EVENT_TYPE( wxEVT_PG_HIGHLIGHTED ) -DEFINE_EVENT_TYPE( wxEVT_PG_RIGHT_CLICK ) -DEFINE_EVENT_TYPE( wxEVT_PG_PAGE_CHANGED ) -DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_EXPANDED ) -DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_COLLAPSED ) -DEFINE_EVENT_TYPE( wxEVT_PG_DOUBLE_CLICK ) - +wxDEFINE_EVENT( wxEVT_PG_SELECTED, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_CHANGING, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_CHANGED, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_HIGHLIGHTED, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_RIGHT_CLICK, wxPropertyGridEvent ); +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 ); +wxDEFINE_EVENT( wxEVT_PG_COL_BEGIN_DRAG, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_COL_DRAGGING, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_COL_END_DRAG, wxPropertyGridEvent ); // ----------------------------------------------------------------------- void wxPropertyGridEvent::Init() { m_validationInfo = NULL; + m_column = 1; m_canVeto = false; m_wasVetoed = false; } @@ -5796,6 +6246,7 @@ wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent& event) m_eventType = event.GetEventType(); m_eventObject = event.m_eventObject; m_pg = event.m_pg; + OnPropertyGridSet(); m_property = event.m_property; m_validationInfo = event.m_validationInfo; m_canVeto = event.m_canVeto; @@ -5804,8 +6255,40 @@ wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent& event) // ----------------------------------------------------------------------- +void wxPropertyGridEvent::OnPropertyGridSet() +{ + if ( !m_pg ) + return; + +#if wxUSE_THREADS + wxCriticalSectionLocker(wxPGGlobalVars->m_critSect); +#endif + m_pg->m_liveEvents.push_back(this); +} + +// ----------------------------------------------------------------------- + wxPropertyGridEvent::~wxPropertyGridEvent() { + if ( m_pg ) + { + #if wxUSE_THREADS + wxCriticalSectionLocker(wxPGGlobalVars->m_critSect); + #endif + + // Use iterate from the back since it is more likely that the event + // being desroyed is at the end of the array. + wxVector& liveEvents = m_pg->m_liveEvents; + + for ( int i = liveEvents.size()-1; i >= 0; i-- ) + { + if ( liveEvents[i] == this ) + { + liveEvents.erase(liveEvents.begin() + i); + break; + } + } + } } // ----------------------------------------------------------------------- @@ -5898,7 +6381,8 @@ wxPGProperty* wxPropertyGridPopulator::Add( const wxString& propClass, m_state->DoInsert(parent, -1, property); if ( propValue ) - property->SetValueFromString( *propValue, wxPG_FULL_VALUE ); + property->SetValueFromString( *propValue, wxPG_FULL_VALUE| + wxPG_PROGRAMMATIC_VALUE ); return property; } @@ -5932,7 +6416,7 @@ wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString else { bool found = false; - if ( idString.length() ) + if ( !idString.empty() ) { wxPGHashMapS2P::iterator it = m_dictIdChoices.find(idString); if ( it != m_dictIdChoices.end() ) @@ -5951,7 +6435,7 @@ wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString int state = 0; bool labelValid = false; - for ( ; it != choicesString.end(); it++ ) + for ( ; it != choicesString.end(); ++it ) { wxChar c = *it; @@ -6008,7 +6492,7 @@ wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString } // Assign to id - if ( idString.length() ) + if ( !idString.empty() ) m_dictIdChoices[idString] = choices.GetData(); } } @@ -6049,7 +6533,7 @@ bool wxPropertyGridPopulator::AddAttribute( const wxString& name, wxString valuel = value.Lower(); wxVariant variant; - if ( type.length() == 0 ) + if ( type.empty() ) { long v;