]> git.saurik.com Git - wxWidgets.git/blobdiff - src/propgrid/propgrid.cpp
Add samples files missing from distribution.
[wxWidgets.git] / src / propgrid / propgrid.cpp
index 6a5a83ab252f152c6e98dd6851b3442af05c9e51..b323f7f4cf6158fc8aa875d7ca97832c9153a8ab 100644 (file)
@@ -63,8 +63,6 @@
 
 #include "wx/timer.h"
 #include "wx/dcbuffer.h"
-#include "wx/clipbrd.h"
-#include "wx/dataobj.h"
 
 #ifdef __WXMSW__
     #include "wx/msw/private.h"
@@ -154,6 +152,15 @@ public:
 IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
 
 
+// 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;
 
 
@@ -176,7 +183,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 +195,13 @@ 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_strInlineHelp = wxS("InlineHelp");
 
-#ifdef __WXDEBUG__
     m_warnings = 0;
-#endif
 }
 
 
@@ -231,46 +237,6 @@ void wxPropertyGridInitGlobalsIfNeeded()
 {
 }
 
-// -----------------------------------------------------------------------
-// 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
 // -----------------------------------------------------------------------
@@ -467,7 +433,6 @@ void wxPropertyGrid::Init1()
     m_propHover = NULL;
     m_eventObject = this;
     m_curFocused = NULL;
-    m_tlwHandler = NULL;
     m_sortFunction = NULL;
     m_inDoPropertyChanged = 0;
     m_inCommitChangesFromEditor = 0;
@@ -498,10 +463,10 @@ void wxPropertyGrid::Init1()
 #endif
 
 #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
@@ -558,19 +523,19 @@ 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;
 
     CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING );
@@ -584,15 +549,15 @@ void wxPropertyGrid::Init2()
     // 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;
+    OnTLPChanging(::wxGetTopLevelParent(this));
 
-       // 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();
 
@@ -625,15 +590,13 @@ wxPropertyGrid::~wxPropertyGrid()
     if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
         m_canvas->ReleaseMouse();
 
-    wxPGTLWHandler* handler = (wxPGTLWHandler*) m_tlwHandler;
-    m_tlp->RemoveEventHandler(handler);
-    delete handler;
+    // Call with NULL to disconnect event handling
+    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 lost!!! ")
+                  wxS("(if you don't want this to happen, close your frames ")
+                  wxS("and dialogs using Close(false).)") );
 
 #if wxPG_DOUBLE_BUFFER
     if ( m_doubleBuffer )
@@ -648,8 +611,8 @@ wxPropertyGrid::~wxPropertyGrid()
     delete m_cursorSizeWE;
 
 #ifndef wxPG_ICON_WIDTH
-       delete m_expandbmp;
-       delete m_collbmp;
+    delete m_expandbmp;
+    delete m_collbmp;
 #endif
 
     // Delete common value records
@@ -828,28 +791,102 @@ void wxPropertyGrid::SetExtraStyle( long exStyle )
 // returns the best acceptable minimal size
 wxSize wxPropertyGrid::DoGetBestSize() const
 {
-    int hei = 15;
-    if ( m_lineHeight > hei )
-        hei = m_lineHeight;
-    wxSize sz = wxSize( 60, hei+40 );
+    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
+                   );
 
+    const wxSize sz = wxSize(60, lineHeight*numLines + 40);
     CacheBestSize(sz);
     return sz;
 }
 
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP )
+{
+    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 = wxScrolledWindow::Reparent(newParent);
+
+    return res;
+}
+
 // -----------------------------------------------------------------------
 // wxPropertyGrid Font and Colour Methods
 // -----------------------------------------------------------------------
 
 void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
 {
-       int x = 0, y = 0;
+    int x = 0, y = 0;
 
     m_captionFont = wxScrolledWindow::GetFont();
 
-       GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
+    GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
     m_subgroup_extramargin = x + (x/2);
-       m_fontHeight = y;
+    m_fontHeight = y;
 
 #if wxPG_USE_RENDERER_NATIVE
     m_iconWidth = wxPG_ICON_WIDTH;
@@ -878,7 +915,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;
 
@@ -1034,10 +1071,10 @@ void wxPropertyGrid::ResetColours()
 bool wxPropertyGrid::SetFont( const wxFont& font )
 {
     // Must disable active editor.
-    ClearSelection(false);
+    DoClearSelection();
 
     bool res = wxScrolledWindow::SetFont( font );
-    if ( res )
+    if ( res && GetParent()) // may not have been Create()ed yet
     {
         CalculateFontAndBitmapStuff( m_vspacing );
         Refresh();
@@ -2131,7 +2168,7 @@ void wxPropertyGrid::Clear()
 
 bool wxPropertyGrid::EnableCategories( bool enable )
 {
-    ClearSelection(false);
+    DoClearSelection();
 
     if ( enable )
     {
@@ -2183,7 +2220,7 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState )
 
     wxPGProperty* oldSelection = m_selected;
 
-    ClearSelection(false);
+    DoClearSelection();
 
     m_pState->m_selected = oldSelection;
 
@@ -2562,7 +2599,7 @@ void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), cons
     }
 #endif
 
-    ::wxMessageBox(msg, _T("Property Error"));
+    ::wxMessageBox(msg, wxT("Property Error"));
 }
 
 // -----------------------------------------------------------------------
@@ -2637,7 +2674,7 @@ bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& W
         wxString msg = m_validationInfo.m_failureMessage;
 
         if ( !msg.length() )
-            msg = _T("You have entered invalid value. Press ESC to cancel editing.");
+            msg = wxT("You have entered invalid value. Press ESC to cancel editing.");
 
         DoShowPropertyError(property, msg);
     }
@@ -2842,10 +2879,10 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
 
     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;
@@ -2891,17 +2928,19 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &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 ) )
             {
                 // If changes, validate them
                 if ( DoEditorValidate() )
                 {
-                    if ( editor->GetValueFromControl( pendingValue, m_selected, wnd ) )
+                    if ( editor->GetValueFromControl( pendingValue,
+                                                      m_selected,
+                                                      wnd ) )
                         valueIsPending = true;
                 }
                 else
@@ -2914,7 +2953,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &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
@@ -2978,24 +3017,25 @@ 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*/ )
+    if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
     {
         //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;
+        int iw = p->OnMeasureImage().x;
+        if ( iw < 1 )
+            iw = wxPG_CUSTOM_IMAGE_WIDTH;
+        imageOffset = p->GetImageOffset(iw);
     }
 
     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
       );
 }
@@ -3205,10 +3245,14 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
 {
     /*
     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 )
@@ -3564,7 +3608,8 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
     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, m_selected, NULL, flags );
 
     return true;
 }
@@ -3617,11 +3662,23 @@ void wxPropertyGrid::RefreshEditor()
 
 // -----------------------------------------------------------------------
 
-// This method is not inline because it called dozens of times
-// (i.e. two-arg function calls create smaller code size).
+bool wxPropertyGrid::SelectProperty( wxPGPropArg id, bool focus )
+{
+    wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
+
+    int flags = wxPG_SEL_DONT_SEND_EVENT;
+    if ( focus )
+        flags |= wxPG_SEL_FOCUS;
+
+    return DoSelectProperty(p, flags);
+}
+
+// -----------------------------------------------------------------------
+
 bool wxPropertyGrid::DoClearSelection()
 {
-    return DoSelectProperty(NULL);
+    // Unlike ClearSelection(), here we send the wxEVT_PG_SELECTED event.
+    return DoSelectProperty(NULL, 0);
 }
 
 // -----------------------------------------------------------------------
@@ -3635,7 +3692,7 @@ bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents )
     // If active editor was inside collapsed section, then disable it
     if ( m_selected && m_selected->IsSomeParent(p) )
     {
-        ClearSelection(false);
+        DoClearSelection();
     }
 
     // Store dont-center-splitter flag 'cause we need to temporarily set it
@@ -3720,7 +3777,7 @@ bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags )
          ( m_selected == p || m_selected->IsSomeParent(p) )
        )
         {
-            ClearSelection(false);
+            DoClearSelection();
         }
 
     m_pState->DoHideProperty(p, hide, flags);
@@ -3748,16 +3805,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);
-        wxFAIL_MSG(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;
 
@@ -3768,7 +3819,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;
@@ -4265,7 +4316,7 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event
                         if ( space )
                         {
                             int tw, th;
-                           GetTextExtent( tipString, &tw, &th, 0, 0 );
+                            GetTextExtent( tipString, &tw, &th, 0, 0 );
                             if ( tw > space )
                             {
                                 SetToolTip( tipString );
@@ -4818,9 +4869,7 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild )
         // Travel and expand/collapse
         int selectDir = -2;
 
-        if ( p->GetChildCount() &&
-             !(p->m_flags & wxPG_PROP_DISABLED)
-           )
+        if ( p->GetChildCount() )
         {
             if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY )
             {
@@ -4931,6 +4980,14 @@ void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) )
 
     if ( newFocused != m_curFocused )
         HandleFocusChange( newFocused );
+
+    //
+    // Check if top-level parent has changed
+    wxWindow* tlp = ::wxGetTopLevelParent(this);
+    if ( tlp != m_tlp )
+    {
+        OnTLPChanging(tlp);
+    }
 }
 
 bool wxPropertyGrid::IsEditorFocused() const
@@ -5030,11 +5087,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();
 }
 
 // -----------------------------------------------------------------------
@@ -5061,15 +5114,18 @@ 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.length() == 0 )
+        name = editorClass->GetName();
 
     // Existing editor under this name?
     wxPGHashMapS2P::iterator vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name);
@@ -5206,7 +5262,6 @@ wxPGChoiceEntry::wxPGChoiceEntry()
 
 wxPGChoicesData::wxPGChoicesData()
 {
-    m_refCount = 1;
 }
 
 wxPGChoicesData::~wxPGChoicesData()