X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b7d1985040f01dd84ed89646aed2ab49f48737bd..63bcc669d81774a91f39054e8ec44f8d7ac6f760:/src/propgrid/propgrid.cpp diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 83462b36a5..18370b90df 100644 --- a/src/propgrid/propgrid.cpp +++ b/src/propgrid/propgrid.cpp @@ -64,10 +64,6 @@ #include "wx/timer.h" #include "wx/dcbuffer.h" -#ifdef __WXMSW__ - #include "wx/msw/private.h" -#endif - // Two pics for the expand / collapse buttons. // Files are not supplied with this project (since it is // recommended to use either custom or native rendering). @@ -398,8 +394,10 @@ bool wxPropertyGrid::Create( wxWindow *parent, const wxString& name ) { - if ( !(style&wxBORDER_MASK) ) - style |= wxSIMPLE_BORDER; + if (!(style&wxBORDER_MASK)) + { + style |= wxBORDER_THEME; + } style |= wxVSCROLL; @@ -428,11 +426,14 @@ void wxPropertyGrid::Init1() m_iFlags = 0; m_pState = NULL; m_wndEditor = m_wndEditor2 = NULL; - m_selected = NULL; - m_selColumn = -1; + m_selColumn = 1; + m_colHover = 1; m_propHover = NULL; + m_labelEditor = NULL; + m_labelEditorProperty = NULL; m_eventObject = this; m_curFocused = NULL; + m_processedEvent = NULL; m_sortFunction = NULL; m_inDoPropertyChanged = 0; m_inCommitChangesFromEditor = 0; @@ -551,7 +552,8 @@ void wxPropertyGrid::Init2() // Hook the top-level parent m_tlp = NULL; - OnTLPChanging(NULL); + m_tlpClosed = NULL; + m_tlpClosedTime = 0; // set virtual size to this window size wxSize wndsize = GetSize(); @@ -560,7 +562,7 @@ void wxPropertyGrid::Init2() m_timeCreated = ::wxGetLocalTimeMillis(); m_canvas = new wxPGCanvas(); - m_canvas->Create(this, 1, wxPoint(0, 0), GetClientSize(), + m_canvas->Create(this, wxID_ANY, wxPoint(0, 0), GetClientSize(), wxWANTS_CHARS | wxCLIP_CHILDREN); m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM ); @@ -580,7 +582,38 @@ 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); @@ -588,33 +621,23 @@ wxPropertyGrid::~wxPropertyGrid() if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) m_canvas->ReleaseMouse(); - // Do TLP check, recommend use of OnTLPChanging() - wxWindow* tlp = ::wxGetTopLevelParent(this); - if ( tlp == m_tlp ) + // Call with NULL to disconnect event handling + if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING ) { - m_tlp->Disconnect( wxEVT_CLOSE_WINDOW, - wxCloseEventHandler(wxPropertyGrid::OnTLPClose), - NULL, this ); - } - else if ( tlp ) - { - wxLogError("Top-level parent of wxPropertyGrid has changed. " - "Consider calling wxPropertyGrid::OnTLPChanging() " - "when appropriate."); - } + OnTLPChanging(NULL); - 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).)") ); + 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 = NULL; - if ( m_iFlags & wxPG_FL_CREATEDSTATE ) delete m_pState; @@ -628,7 +651,9 @@ wxPropertyGrid::~wxPropertyGrid() // Delete common value records for ( i=0; im_selection; + DoSetSelection(selection, wxPG_SEL_FORCE); } } // ----------------------------------------------------------------------- +bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags ) +{ + wxCHECK( prop, false ); + + if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) ) + return DoSelectProperty(prop, selFlags); + + wxArrayPGProperty& selection = m_pState->m_selection; + + if ( !selection.size() ) + { + return DoSelectProperty(prop, selFlags); + } + else + { + // For categories, only one can be selected at a time + if ( prop->IsCategory() || selection[0]->IsCategory() ) + return true; + + selection.push_back(prop); + + if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) + { + SendEvent( wxEVT_PG_SELECTED, prop, NULL ); + } + + DrawItem(prop); + } + + return true; +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags ) +{ + wxCHECK( prop, false ); + bool res; + + wxArrayPGProperty& selection = m_pState->m_selection; + if ( selection.size() <= 1 ) + { + res = DoSelectProperty(NULL, selFlags); + } + else + { + m_pState->DoRemoveFromSelection(prop); + DrawItem(prop); + res = true; + } + + return res; +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty* prop, + unsigned int colIndex, + unsigned int selFlags ) +{ + // + // NB: Enable following if label editor background colour is + // ever changed to any other than m_colSelBack. + // + // We use this workaround to prevent visible flicker when editing + // a cell. Atleast on wxMSW, there is a difficult to find + // (and perhaps prevent) redraw somewhere between making property + // selected and enabling label editing. + // + //wxColour prevColSelBack = m_colSelBack; + //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); + + bool res; + + if ( colIndex == 1 ) + { + res = DoSelectProperty(prop, selFlags); + } + else + { + // send event + DoClearSelection(false, wxPG_SEL_NO_REFRESH); + + if ( m_pState->m_editableColumns.Index(colIndex) == wxNOT_FOUND ) + { + res = DoAddToSelection(prop, selFlags); + } + else + { + res = DoAddToSelection(prop, selFlags|wxPG_SEL_NO_REFRESH); + + DoBeginLabelEdit(colIndex, selFlags); + } + } + + //m_colSelBack = prevColSelBack; + return res; +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop, + unsigned int colIndex, + wxMouseEvent* mouseEvent, + int selFlags ) +{ + bool alreadySelected = m_pState->DoIsPropertySelected(prop); + bool res = true; + bool addToExistingSelection; + + if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION ) + { + if ( mouseEvent ) + { + if ( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN || + mouseEvent->GetEventType() == wxEVT_RIGHT_UP ) + { + // Allow right-click for context menu without + // disturbing the selection. + if ( GetSelectedProperties().size() <= 1 || + !alreadySelected ) + return DoSelectAndEdit(prop, colIndex, selFlags); + return true; + } + else + { + addToExistingSelection = mouseEvent->ShiftDown(); + } + } + else + { + addToExistingSelection = false; + } + } + else + { + addToExistingSelection = false; + } + + if ( addToExistingSelection ) + { + if ( !alreadySelected ) + { + res = DoAddToSelection(prop, selFlags); + } + else if ( GetSelectedProperties().size() > 1 ) + { + res = DoRemoveFromSelection(prop, selFlags); + } + } + else + { + res = 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 + { + event.Skip(); + } +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags ) +{ + if ( !m_labelEditor ) + return; + + wxPGProperty* prop = m_labelEditorProperty; + wxASSERT(prop); + + if ( commit ) + { + if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) + { + // wxPG_SEL_NOVALIDATE is passed correctly in selFlags + if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING, + prop, NULL, selFlags, + m_selColumn ) ) + return; + } + + wxString text = m_labelEditor->GetValue(); + wxPGCell* cell = NULL; + if ( prop->HasCell(m_selColumn) ) + { + cell = &prop->GetCell(m_selColumn); + } + else + { + if ( m_selColumn == 0 ) + prop->SetLabel(text); + else + cell = &prop->GetOrCreateCell(m_selColumn); + } + + if ( cell ) + cell->SetText(text); + } + + m_selColumn = 1; + + DestroyEditorWnd(m_labelEditor); + m_labelEditor = NULL; + m_labelEditorProperty = NULL; + + DrawItem(prop); +} + +// ----------------------------------------------------------------------- + void wxPropertyGrid::SetExtraStyle( long exStyle ) { + if ( exStyle & wxPG_EX_ENABLE_TLP_TRACKING ) + OnTLPChanging(::wxGetTopLevelParent(this)); + else + OnTLPChanging(NULL); + if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) { #if defined(__WXMSW__) @@ -811,36 +1185,58 @@ wxSize wxPropertyGrid::DoGetBestSize() const 10 ); - const wxSize sz = wxSize(60, lineHeight*numLines + 40); + 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 ) { - wxASSERT_MSG( m_tlp == ::wxGetTopLevelParent(this), - "You must call OnTLPChanging() before the " - "top-level parent has changed."); - m_tlp->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxPropertyGrid::OnTLPClose), NULL, this ); + m_tlpClosed = m_tlp; + m_tlpClosedTime = currentTime; } - if ( !newTLP ) - newTLP = ::wxGetTopLevelParent(this); + 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; - m_tlp->Connect( wxEVT_CLOSE_WINDOW, - wxCloseEventHandler(wxPropertyGrid::OnTLPClose), - NULL, this ); } // ----------------------------------------------------------------------- @@ -848,12 +1244,17 @@ void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP ) void wxPropertyGrid::OnTLPClose( wxCloseEvent& event ) { // ClearSelection forces value validation/commit. - if ( event.CanVeto() && !ClearSelection() ) + 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(); } @@ -1065,10 +1466,10 @@ void wxPropertyGrid::ResetColours() bool wxPropertyGrid::SetFont( const wxFont& font ) { // Must disable active editor. - ClearSelection(false); + DoClearSelection(); bool res = wxScrolledWindow::SetFont( font ); - if ( res && GetParent()) // may not have been Create()ed yet + if ( res && GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant { CalculateFontAndBitmapStuff( m_vspacing ); Refresh(); @@ -1678,7 +2079,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) != 0; - bool isEnabled = IsEnabled(); + bool isPgEnabled = IsEnabled(); // // Prepare some pens and brushes that are often changed to. @@ -1689,6 +2090,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); @@ -1702,7 +2109,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, dc.DrawRectangle(-1-xRelMod,firstItemTopY-1,x+2,lastItemBottomY-firstItemTopY+2); } - const wxPGProperty* selected = m_selected; + const wxPGProperty* firstSelected = GetSelection(); const wxPropertyGridPageState* state = m_pState; #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT @@ -1776,8 +2183,28 @@ 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; @@ -1802,7 +2229,9 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, wxColour rowFgCol; wxColour rowBgCol; - if ( p != selected ) + bool isSelected = state->DoIsPropertySelected(p); + + if ( !isSelected ) { // Disabled may get different colour. if ( !p->IsEnabled() ) @@ -1814,6 +2243,11 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, } else { +#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT + if ( p == firstSelected ) + wasSelectedPainted = true; +#endif + renderFlags |= wxPGCellRenderer::Selected; if ( !p->IsCategory() ) @@ -1821,25 +2255,23 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, renderFlags |= wxPGCellRenderer::DontUseCellFgCol | wxPGCellRenderer::DontUseCellBgCol; -#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT - wasSelectedPainted = true; -#endif - - // Selected gets different colour. - if ( reallyFocused ) + if ( reallyFocused && p == firstSelected ) { rowFgCol = m_colSelFore; - rowBgCol = m_colSelBack; + rowBgCol = selBackCol; } - else if ( isEnabled ) + else if ( isPgEnabled ) { rowFgCol = m_colPropFore; - rowBgCol = m_colMargin; + if ( p == firstSelected ) + rowBgCol = m_colMargin; + else + rowBgCol = selBackCol; } else { rowFgCol = m_colDisPropFore; - rowBgCol = m_colSelBack; + rowBgCol = selBackCol; } } } @@ -1920,24 +2352,47 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, { cellRect.width = nextCellWidth - 1; - bool ctrlCell = false; + wxWindow* cellEditor = NULL; int cellRenderFlags = renderFlags; - // Tree Item Button + // Tree Item Button (must be drawn before clipping is set up) if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) DrawExpanderButton( dc, butRect, p ); // Background - if ( p == selected && m_wndEditor && ci == 1 ) + if ( isSelected && (ci == 1 || ci == m_selColumn) ) { - wxColour editorBgCol = GetEditorControl()->GetBackgroundColour(); - dc.SetBrush(editorBgCol); - dc.SetPen(editorBgCol); - dc.SetTextForeground(m_colPropFore); - dc.DrawRectangle(cellRect); - - if ( m_dragStatus == 0 && !(m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) ) - ctrlCell = true; + if ( p == firstSelected ) + { + 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) ) + cellEditor = NULL; + } + else + { + dc.SetBrush(m_colPropBack); + dc.SetPen(m_colPropBack); + dc.SetTextForeground(m_colDisPropFore); + if ( p->IsEnabled() ) + dc.SetTextForeground(rowFgCol); + else + dc.SetTextForeground(m_colDisPropFore); + } } else { @@ -1959,7 +2414,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, cellRect.width -= textXAdd; // Foreground - if ( !ctrlCell ) + if ( !cellEditor ) { wxPGCellRenderer* renderer; int cmnVal = p->GetCommonValue(); @@ -2032,7 +2487,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(); @@ -2072,8 +2527,12 @@ void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 ) void wxPropertyGrid::RefreshProperty( wxPGProperty* p ) { - if ( p == m_selected ) - DoSelectProperty(p, wxPG_SEL_FORCE); + if ( m_pState->DoIsPropertySelected(p) ) + { + // NB: We must copy the selection. + wxArrayPGProperty selection = m_pState->m_selection; + DoSetSelection(selection, wxPG_SEL_FORCE); + } DrawItemAndChildren(p); } @@ -2112,7 +2571,8 @@ void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p ) return; // Update child control. - if ( m_selected && m_selected->GetParent() == p ) + wxPGProperty* selected = GetSelection(); + if ( selected && selected->GetParent() == p ) RefreshEditor(); const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem(); @@ -2162,7 +2622,7 @@ void wxPropertyGrid::Clear() bool wxPropertyGrid::EnableCategories( bool enable ) { - ClearSelection(false); + DoClearSelection(); if ( enable ) { @@ -2212,11 +2672,13 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) if ( pNewState == m_pState ) return; - wxPGProperty* oldSelection = m_selected; + wxArrayPGProperty oldSelection = m_pState->m_selection; - ClearSelection(false); + // 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(); @@ -2257,9 +2719,9 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) // Refresh, if not frozen. m_pState->PrepareAfterItemsAdded(); - // Reselect - if ( m_pState->m_selected ) - DoSelectProperty( m_pState->m_selected ); + // Reselect (Use SetSelection() instead of Do-variant so that + // events won't be sent). + SetSelection(m_pState->m_selection); RecalculateVirtualSize(0); Refresh(); @@ -2283,7 +2745,7 @@ void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int spli if ( refresh ) { - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); Refresh(); @@ -2354,14 +2816,16 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) return false; } + wxPGProperty* selected = GetSelection(); + if ( m_wndEditor && IsEditorsValueModified() && (m_iFlags & wxPG_FL_INITIALIZED) && - m_selected ) + selected ) { m_inCommitChangesFromEditor = 1; - wxVariant variant(m_selected->GetValueRef()); + wxVariant variant(selected->GetValueRef()); bool valueIsPending = false; // JACS - necessary to avoid new focus being found spuriously within OnIdle @@ -2374,10 +2838,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; } @@ -2403,18 +2870,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(); } @@ -2517,7 +2984,7 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) ) { - if ( changedProperty == m_selected ) + if ( changedProperty == GetSelection() ) { wxWindow* editor = GetEditorControl(); wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) ); @@ -2551,7 +3018,8 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue if ( flags & SendEvtChanging ) { // SendEvent returns true if event was vetoed - if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue, 0 ) ) + if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, + &evtChangingValue ) ) return false; } @@ -2593,7 +3061,7 @@ void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), cons } #endif - ::wxMessageBox(msg, _T("Property Error")); + ::wxMessageBox(msg, wxT("Property Error")); } // ----------------------------------------------------------------------- @@ -2611,7 +3079,7 @@ bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property, // // For non-wxTextCtrl editors, we do need to revert the value if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) && - property == m_selected ) + property == GetSelection() ) { property->GetEditorClass()->UpdateControl(property, editor); } @@ -2650,7 +3118,7 @@ bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& W DrawItemAndChildren(property); - if ( property == m_selected ) + if ( property == GetSelection() ) { SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL); @@ -2668,7 +3136,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); } @@ -2689,7 +3157,7 @@ void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property ) ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL); - if ( property == m_selected && GetEditorControl() ) + if ( property == GetSelection() && GetEditorControl() ) { // Calling this will recreate the control, thus resetting its colour RefreshProperty(property); @@ -2710,6 +3178,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) return true; wxWindow* editor = GetEditorControl(); + wxPGProperty* selected = GetSelection(); m_pState->m_anyModified = 1; @@ -2736,7 +3205,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) if ( !(p->m_flags & wxPG_PROP_MODIFIED) ) { p->m_flags |= wxPG_PROP_MODIFIED; - if ( p == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) ) + if ( p == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) ) { if ( editor ) SetCurControlBoldFont(); @@ -2753,7 +3222,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(); @@ -2792,12 +3261,12 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) while ( pwc != changedProperty ) { - SendEvent( wxEVT_PG_CHANGED, pwc, NULL, selFlags ); + SendEvent( wxEVT_PG_CHANGED, pwc, NULL ); pwc = pwc->GetParent(); } } - SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL, selFlags ); + SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL ); m_inDoPropertyChanged = 0; @@ -2861,11 +3330,16 @@ bool wxPropertyGrid::DoEditorValidate() void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) { - wxPGProperty* selected = m_selected; + wxPGProperty* selected = GetSelection(); // Somehow, event is handled after property has been deselected. // Possibly, but very rare. - if ( !selected ) + if ( !selected || + selected->HasFlag(wxPG_PROP_BEING_DELETED) || + // 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; if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT ) @@ -2933,7 +3407,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) if ( DoEditorValidate() ) { if ( editor->GetValueFromControl( pendingValue, - m_selected, + selected, wnd ) ) valueIsPending = true; } @@ -2960,7 +3434,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) } if ( !validationFailure && valueIsPending ) - if ( !PerformValidation(m_selected, pendingValue) ) + if ( !PerformValidation(selected, pendingValue) ) validationFailure = true; if ( validationFailure) @@ -3193,6 +3667,17 @@ void wxPropertyGrid::SetupChildEventHandling( wxWindow* argWnd ) NULL, this); } +void wxPropertyGrid::DestroyEditorWnd( wxWindow* wnd ) +{ + if ( !wnd ) + return; + + wnd->Hide(); + + // Do not free editors immediately (for sake of processing events) + wxPendingDelete.Append(wnd); +} + void wxPropertyGrid::FreeEditors() { // @@ -3220,7 +3705,7 @@ void wxPropertyGrid::FreeEditors() wxEvtHandler* handler = m_wndEditor2->PopEventHandler(false); m_wndEditor2->Hide(); wxPendingDelete.Append( handler ); - wxPendingDelete.Append( m_wndEditor2 ); + DestroyEditorWnd(m_wndEditor2); m_wndEditor2 = NULL; } @@ -3229,7 +3714,7 @@ void wxPropertyGrid::FreeEditors() wxEvtHandler* handler = m_wndEditor->PopEventHandler(false); m_wndEditor->Hide(); wxPendingDelete.Append( handler ); - wxPendingDelete.Append( m_wndEditor ); + DestroyEditorWnd(m_wndEditor); m_wndEditor = NULL; } } @@ -3239,10 +3724,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 ) @@ -3250,17 +3739,29 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_inDoSelectProperty = 1; - wxPGProperty* prev = m_selected; - if ( !m_pState ) { m_inDoSelectProperty = 0; return false; } + wxArrayPGProperty prevSelection = m_pState->m_selection; + wxPGProperty* prevFirstSel; + + if ( prevSelection.size() > 0 ) + prevFirstSel = prevSelection[0]; + else + prevFirstSel = NULL; + + if ( prevFirstSel && prevFirstSel->HasFlag(wxPG_PROP_BEING_DELETED) ) + prevFirstSel = NULL; + + // 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" ); @@ -3275,9 +3776,7 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) { m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); m_editorFocused = 0; - m_selected = p; - m_selColumn = 1; - m_pState->m_selected = p; + m_pState->DoSetSelection(p); // If frozen, always free controls. But don't worry, as Thaw will // recall SelectProperty to recreate them. @@ -3289,7 +3788,9 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) else { // Is it the same? - if ( m_selected == p && !(flags & wxPG_SEL_FORCE) ) + if ( prevFirstSel == p && + prevSelection.size() <= 1 && + !(flags & wxPG_SEL_FORCE) ) { // Only set focus if not deselecting if ( p ) @@ -3314,13 +3815,12 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) // // First, deactivate previous - if ( m_selected ) + if ( prevFirstSel ) { - - OnValidationFailureReset(m_selected); + OnValidationFailureReset(prevFirstSel); // Must double-check if this is an selected in case of forceswitch - if ( p != prev ) + if ( p != prevFirstSel ) { if ( !CommitChangesFromEditor(flags) ) { @@ -3333,13 +3833,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } FreeEditors(); - m_selColumn = -1; - - m_selected = NULL; - m_pState->m_selected = NULL; - - // We need to always fully refresh the grid here - Refresh(false); m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); EditorsValueWasNotModified(); @@ -3347,6 +3840,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 ) + if ( p != prevFirstSel ) m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED); wxASSERT( m_wndEditor == NULL ); @@ -3380,10 +3879,6 @@ 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 const wxPGEditor* editor = p->GetEditorClass(); wxCHECK_MSG(editor, false, @@ -3429,18 +3924,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) 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; @@ -3453,15 +3936,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) @@ -3487,19 +3961,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); @@ -3533,13 +3994,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 { @@ -3598,7 +4057,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, p, NULL ); return true; } @@ -3607,14 +4067,16 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) bool wxPropertyGrid::UnfocusEditor() { - if ( !m_selected || !m_wndEditor || m_frozen ) + wxPGProperty* selected = GetSelection(); + + if ( !selected || !m_wndEditor || m_frozen ) return true; if ( !CommitChangesFromEditor(0) ) return false; SetFocusOnCanvas(); - DrawItem(m_selected); + DrawItem(selected); return true; } @@ -3623,8 +4085,8 @@ bool wxPropertyGrid::UnfocusEditor() void wxPropertyGrid::RefreshEditor() { - wxPGProperty* p = m_selected; - if ( !p ) + wxPGProperty* p = GetSelection(); + if ( !p ) return; wxWindow* wnd = GetEditorControl(); @@ -3651,11 +4113,15 @@ 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::DoClearSelection() +bool wxPropertyGrid::SelectProperty( wxPGPropArg id, bool focus ) { - return DoSelectProperty(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); } // ----------------------------------------------------------------------- @@ -3665,11 +4131,12 @@ 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) ) { - ClearSelection(false); + DoClearSelection(); } // Store dont-center-splitter flag 'cause we need to temporarily set it @@ -3750,12 +4217,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) ) { - ClearSelection(false); + if ( !DoRemoveFromSelection(p, flags) ) + return false; + selRemoveCount += 1; } + } m_pState->DoHideProperty(p, hide, flags); @@ -3833,11 +4306,14 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) m_width = width; m_height = height; - m_canvas->SetSize( x, y ); + // Explicitly pass the position - works around a bug in wxWidgets when the property grid + // has a native XP border and a contained window creeps up-and-left when size is set without + // the position. + m_canvas->SetSize( 0, 0, x, y ); m_pState->CheckColumnWidths(); - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE; @@ -3931,22 +4407,36 @@ void wxPropertyGrid::SetFocusOnCanvas() // selFlags uses same values DoSelectProperty's flags // Returns true if event was vetoed. -bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, wxVariant* pValue, unsigned int WXUNUSED(selFlags) ) +bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, + wxVariant* pValue, + unsigned int selFlags, + unsigned int column ) { // Send property grid event of specific type and with specific property wxPropertyGridEvent evt( eventType, m_eventObject->GetId() ); evt.SetPropertyGrid(this); evt.SetEventObject(m_eventObject); evt.SetProperty(p); - 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); + m_processedEvent = &evt; + m_eventObject->HandleWindowEvent(evt); + m_processedEvent = NULL; return evt.WasVetoed(); } @@ -3996,7 +4486,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. @@ -4016,7 +4508,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); @@ -4044,9 +4539,13 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even } else if ( m_dragStatus == 0 ) { - // - // Begin draggin the splitter - // + // + // Begin draggin the splitter + // + + // send event + DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); + if ( m_wndEditor ) { // Changes must be committed here or the @@ -4107,15 +4606,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 ); @@ -4127,16 +4626,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 ); @@ -4186,6 +4685,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) && @@ -4203,7 +4704,7 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false ); state->m_fSplitterX = (float) newSplitterX; - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); Update(); @@ -4362,6 +4863,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; } @@ -4411,8 +4944,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 ) { @@ -4636,8 +5170,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(); } @@ -4716,14 +5252,25 @@ 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 ); } void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) @@ -4735,6 +5282,7 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) wxCHECK2(!m_frozen, return); // Travelsal between items, collapsing/expanding, etc. + wxPGProperty* selected = GetSelection(); int keycode = event.GetKeyCode(); bool editorFocused = IsEditorFocused(); @@ -4751,7 +5299,7 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) { if ( !editorFocused && m_wndEditor ) { - DoSelectProperty( m_selected, wxPG_SEL_FOCUS ); + DoSelectProperty( selected, wxPG_SEL_FOCUS ); } else { @@ -4810,12 +5358,13 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) EditorsValueWasNotModified(); // Update the control as well - m_selected->GetEditorClass()->SetControlStringValue( m_selected, - GetEditorControl(), - m_selected->GetDisplayedString() ); + selected->GetEditorClass()-> + SetControlStringValue( selected, + GetEditorControl(), + selected->GetDisplayedString() ); } - OnValidationFailureReset(m_selected); + OnValidationFailureReset(selected); UnfocusEditor(); return; @@ -4835,13 +5384,13 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) bool wasHandled = false; - if ( m_selected ) + if ( selected ) { // Show dialog? if ( ButtonTriggerKeyTest(action, event) ) return; - wxPGProperty* p = m_selected; + wxPGProperty* p = selected; // Travel and expand/collapse int selectDir = -2; @@ -4957,6 +5506,15 @@ 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); + } } bool wxPropertyGrid::IsEditorFocused() const @@ -5034,8 +5592,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 ); } } @@ -5231,7 +5790,6 @@ wxPGChoiceEntry::wxPGChoiceEntry() wxPGChoicesData::wxPGChoicesData() { - m_refCount = 1; } wxPGChoicesData::~wxPGChoicesData() @@ -5292,13 +5850,15 @@ wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED, wxPropertyGridEvent ); wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED, wxPropertyGridEvent ); wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEvent ); wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent ); - +wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING, wxPropertyGridEvent ); // ----------------------------------------------------------------------- void wxPropertyGridEvent::Init() { m_validationInfo = NULL; + m_column = 1; m_canVeto = false; m_wasVetoed = false; } @@ -5320,6 +5880,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; @@ -5328,8 +5889,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; + } + } + } } // -----------------------------------------------------------------------