#include "wx/timer.h"
#include "wx/dcbuffer.h"
-#include "wx/clipbrd.h"
-#include "wx/dataobj.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
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;
wxVariant v;
- // Prepare some shared variants
+ // Prepare some shared variants
m_vEmptyString = wxString();
m_vZero = (long) 0;
m_vMinusOne = (long) -1;
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
}
{
}
-// -----------------------------------------------------------------------
-// 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
// -----------------------------------------------------------------------
const wxString& name )
{
- if ( !(style&wxBORDER_MASK) )
- style |= wxSIMPLE_BORDER;
+ if (!(style&wxBORDER_MASK))
+ {
+ style |= wxBORDER_THEME;
+ }
style |= wxVSCROLL;
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_tlwHandler = NULL;
+ m_processedEvent = NULL;
m_sortFunction = NULL;
m_inDoPropertyChanged = 0;
m_inCommitChangesFromEditor = 0;
#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
#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 );
// 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(),
+ m_canvas->Create(this, wxID_ANY, wxPoint(0, 0), GetClientSize(),
wxWANTS_CHARS | wxCLIP_CHILDREN);
m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM );
{
size_t i;
- DoSelectProperty(NULL);
+#if wxUSE_THREADS
+ wxCriticalSectionLocker(wxPGGlobalVars->m_critSect);
+#endif
+
+ //
+ // Remove grid and property pointers from live wxPropertyGridEvents.
+ for ( i=0; i<m_liveEvents.size(); i++ )
+ {
+ wxPropertyGridEvent* evt = m_liveEvents[i];
+ evt->SetPropertyGrid(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();
- 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 = NULL;
-
if ( m_iFlags & wxPG_FL_CREATEDSTATE )
delete m_pState;
delete m_cursorSizeWE;
#ifndef wxPG_ICON_WIDTH
- delete m_expandbmp;
- delete m_collbmp;
+ delete m_expandbmp;
+ delete m_collbmp;
#endif
// Delete common value records
for ( i=0; i<m_commonValues.size(); i++ )
{
- delete GetCommonValue(i);
+ // Use temporary variable to work around possible strange VC6 (asserts because m_size is zero)
+ wxPGCommonValue* value = m_commonValues[i];
+ delete value;
}
}
#endif
// Force property re-selection
- if ( m_selected )
- DoSelectProperty(m_selected, wxPG_SEL_FORCE);
+ // NB: We must copy the selection.
+ wxArrayPGProperty selection = m_pState->m_selection;
+ DoSetSelection(selection, wxPG_SEL_FORCE);
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags )
+{
+ wxCHECK( prop, false );
+
+ if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) )
+ return DoSelectProperty(prop, selFlags);
+
+ wxArrayPGProperty& selection = m_pState->m_selection;
+
+ if ( !selection.size() )
+ {
+ return DoSelectProperty(prop, selFlags);
+ }
+ else
+ {
+ // For categories, only one can be selected at a time
+ if ( prop->IsCategory() || selection[0]->IsCategory() )
+ return true;
+
+ selection.push_back(prop);
+
+ if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
+ {
+ SendEvent( wxEVT_PG_SELECTED, prop, NULL );
+ }
+
+ 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__)
// 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
+ );
+
+ wxClientDC dc(const_cast<wxPropertyGrid *>(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 = 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;
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;
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 if SetFont called from SetWindowVariant
{
CalculateFontAndBitmapStuff( m_vspacing );
Refresh();
bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) != 0;
- bool isEnabled = IsEnabled();
+ bool isPgEnabled = IsEnabled();
//
// Prepare some pens and brushes that are often changed to.
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);
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
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;
wxColour rowFgCol;
wxColour rowBgCol;
- if ( p != selected )
+ bool isSelected = state->DoIsPropertySelected(p);
+
+ if ( !isSelected )
{
// Disabled may get different colour.
if ( !p->IsEnabled() )
}
else
{
+#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+ if ( p == firstSelected )
+ wasSelectedPainted = true;
+#endif
+
renderFlags |= wxPGCellRenderer::Selected;
if ( !p->IsCategory() )
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;
}
}
}
{
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
{
cellRect.width -= textXAdd;
// Foreground
- if ( !ctrlCell )
+ if ( !cellEditor )
{
wxPGCellRenderer* renderer;
int cmnVal = p->GetCommonValue();
// 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();
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);
}
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();
bool wxPropertyGrid::EnableCategories( bool enable )
{
- ClearSelection(false);
+ DoClearSelection();
if ( enable )
{
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();
// 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();
if ( refresh )
{
- if ( m_selected )
+ if ( GetSelection() )
CorrectEditorWidgetSizeX();
Refresh();
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
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;
}
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();
}
if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
{
- if ( changedProperty == m_selected )
+ if ( changedProperty == GetSelection() )
{
wxWindow* editor = GetEditorControl();
wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) );
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;
}
}
#endif
- ::wxMessageBox(msg, _T("Property Error"));
+ ::wxMessageBox(msg, wxT("Property Error"));
}
// -----------------------------------------------------------------------
//
// 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);
}
DrawItemAndChildren(property);
- if ( property == m_selected )
+ if ( property == GetSelection() )
{
SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
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);
}
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);
return true;
wxWindow* editor = GetEditorControl();
+ wxPGProperty* selected = GetSelection();
m_pState->m_anyModified = 1;
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();
{
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();
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;
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 )
if ( DoEditorValidate() )
{
if ( editor->GetValueFromControl( pendingValue,
- m_selected,
+ selected,
wnd ) )
valueIsPending = true;
}
}
if ( !validationFailure && valueIsPending )
- if ( !PerformValidation(m_selected, pendingValue) )
+ if ( !PerformValidation(selected, pendingValue) )
validationFailure = true;
if ( validationFailure)
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()
{
//
wxEvtHandler* handler = m_wndEditor2->PopEventHandler(false);
m_wndEditor2->Hide();
wxPendingDelete.Append( handler );
- wxPendingDelete.Append( m_wndEditor2 );
+ DestroyEditorWnd(m_wndEditor2);
m_wndEditor2 = NULL;
}
wxEvtHandler* handler = m_wndEditor->PopEventHandler(false);
m_wndEditor->Hide();
wxPendingDelete.Append( handler );
- wxPendingDelete.Append( m_wndEditor );
+ DestroyEditorWnd(m_wndEditor);
m_wndEditor = NULL;
}
}
{
/*
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 )
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" );
{
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.
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 )
//
// 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) )
{
}
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();
SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
+ m_pState->DoSetSelection(p);
+
+ // Redraw unselected
+ for ( unsigned int i=0; i<prevSelection.size(); i++ )
+ {
+ DrawItem(prevSelection[i]);
+ }
+
//
// Then, activate the one given.
if ( p )
int splitterX = GetSplitterPosition();
m_editorFocused = 0;
- m_selected = p;
- m_pState->m_selected = p;
m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
- if ( p != prev )
+ if ( p != prevFirstSel )
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
wxASSERT( m_wndEditor == NULL );
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,
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;
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)
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);
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
{
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;
}
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;
}
void wxPropertyGrid::RefreshEditor()
{
- wxPGProperty* p = m_selected;
- if ( !p )
+ wxPGProperty* p = GetSelection();
+ if ( !p )
return;
wxWindow* wnd = GetEditorControl();
// -----------------------------------------------------------------------
-// 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);
}
// -----------------------------------------------------------------------
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
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; i<selection.size(); i++ )
+ {
+ wxPGProperty* selected = selection[i];
+ if ( selected == p || selected->IsSomeParent(p) )
{
- ClearSelection(false);
+ if ( !DoRemoveFromSelection(p, flags) )
+ return false;
+ selRemoveCount += 1;
}
+ }
m_pState->DoHideProperty(p, hide, flags);
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;
GetClientSize(&width,&height);
// Now adjust virtual size.
- SetVirtualSize(x, y);
+ SetVirtualSize(x, y);
int xAmount = 0;
int xPos = 0;
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;
// 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();
}
)
)
{
- if ( !DoSelectProperty( p ) )
+ if ( !AddToSelectionFromInputEvent( p,
+ columnHit,
+ &event ) )
return res;
// On double-click, expand/collapse.
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);
}
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
// -----------------------------------------------------------------------
-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 );
// -----------------------------------------------------------------------
-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 );
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) &&
state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false );
state->m_fSplitterX = (float) newSplitterX;
- if ( m_selected )
+ if ( GetSelection() )
CorrectEditorWidgetSizeX();
Update();
if ( space )
{
int tw, th;
- GetTextExtent( tipString, &tw, &th, 0, 0 );
+ GetTextExtent( tipString, &tw, &th, 0, 0 );
if ( tw > space )
{
SetToolTip( tipString );
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;
}
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 )
{
// 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();
}
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 )
wxCHECK2(!m_frozen, return);
// Travelsal between items, collapsing/expanding, etc.
+ wxPGProperty* selected = GetSelection();
int keycode = event.GetKeyCode();
bool editorFocused = IsEditorFocused();
{
if ( !editorFocused && m_wndEditor )
{
- DoSelectProperty( m_selected, wxPG_SEL_FOCUS );
+ DoSelectProperty( selected, wxPG_SEL_FOCUS );
}
else
{
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;
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;
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
}
// 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 );
}
}
// -----------------------------------------------------------------------
// 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);
wxPGChoicesData::wxPGChoicesData()
{
- m_refCount = 1;
}
wxPGChoicesData::~wxPGChoicesData()
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;
}
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;
// -----------------------------------------------------------------------
+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<wxPropertyGridEvent*>& liveEvents = m_pg->m_liveEvents;
+
+ for ( int i = liveEvents.size()-1; i >= 0; i-- )
+ {
+ if ( liveEvents[i] == this )
+ {
+ liveEvents.erase(liveEvents.begin() + i);
+ break;
+ }
+ }
+ }
}
// -----------------------------------------------------------------------