#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).
{
}
-// -----------------------------------------------------------------------
-// wxPGCanvas
-// -----------------------------------------------------------------------
-
-//
-// wxPGCanvas acts as a graphics sub-window of the
-// wxScrolledWindow that wxPropertyGrid is.
-//
-class wxPGCanvas : public wxPanel
-{
-public:
- wxPGCanvas() : wxPanel()
- {
- }
- virtual ~wxPGCanvas() { }
-
-protected:
- void OnMouseMove( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseMove( event );
- }
-
- void OnMouseClick( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseClick( event );
- }
-
- void OnMouseUp( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseUp( event );
- }
-
- void OnMouseRightClick( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseRightClick( event );
- }
-
- void OnMouseDoubleClick( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseDoubleClick( event );
- }
-
- void OnKey( wxKeyEvent& event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnKey( event );
- }
-
- void OnPaint( wxPaintEvent& event );
-
- // Always be focussable, even with child windows
- virtual void SetCanFocus(bool WXUNUSED(canFocus))
- { wxPanel::SetCanFocus(true); }
-
-
-private:
- DECLARE_EVENT_TABLE()
- DECLARE_ABSTRACT_CLASS(wxPGCanvas)
-};
-
-
-IMPLEMENT_ABSTRACT_CLASS(wxPGCanvas,wxPanel)
-
-BEGIN_EVENT_TABLE(wxPGCanvas, wxPanel)
- EVT_MOTION(wxPGCanvas::OnMouseMove)
- EVT_PAINT(wxPGCanvas::OnPaint)
- EVT_LEFT_DOWN(wxPGCanvas::OnMouseClick)
- EVT_LEFT_UP(wxPGCanvas::OnMouseUp)
- EVT_RIGHT_UP(wxPGCanvas::OnMouseRightClick)
- EVT_LEFT_DCLICK(wxPGCanvas::OnMouseDoubleClick)
- EVT_KEY_DOWN(wxPGCanvas::OnKey)
-END_EVENT_TABLE()
-
-
-void wxPGCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
-{
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- wxASSERT( pg->IsKindOf(CLASSINFO(wxPropertyGrid)) );
-
- wxPaintDC dc(this);
-
- // Don't paint after destruction has begun
- if ( !(pg->GetInternalFlags() & wxPG_FL_INITIALIZED) )
- return;
-
- // Update everything inside the box
- wxRect r = GetUpdateRegion().GetBox();
-
- // FIXME: This is just a workaround for a bug that causes splitters not
- // to paint when other windows are being dragged over the grid.
- wxRect fullRect = GetRect();
- r.x = fullRect.x;
- r.width = fullRect.width;
-
- // Repaint this rectangle
- pg->DrawItems( dc, r.y, r.y + r.height, &r );
-
- // We assume that the size set when grid is shown
- // is what is desired.
- pg->SetInternalFlag(wxPG_FL_GOOD_SIZE_SET);
-}
-
// -----------------------------------------------------------------------
// wxPropertyGrid
// -----------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
EVT_IDLE(wxPropertyGrid::OnIdle)
- EVT_MOTION(wxPropertyGrid::OnMouseMoveBottom)
EVT_PAINT(wxPropertyGrid::OnPaint)
EVT_SIZE(wxPropertyGrid::OnResize)
EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry)
EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent)
EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent)
EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged)
+ EVT_MOTION(wxPropertyGrid::OnMouseMove)
+ EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick)
+ EVT_LEFT_UP(wxPropertyGrid::OnMouseUp)
+ EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick)
+ EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick)
+ EVT_KEY_DOWN(wxPropertyGrid::OnKey)
END_EVENT_TABLE()
-
// -----------------------------------------------------------------------
wxPropertyGrid::wxPropertyGrid()
const wxString& name )
{
- if ( !(style&wxBORDER_MASK) )
- style |= wxSIMPLE_BORDER;
+ if (!(style&wxBORDER_MASK))
+ {
+ style |= wxBORDER_THEME;
+ }
style |= wxVSCROLL;
m_labelEditorProperty = NULL;
m_eventObject = this;
m_curFocused = NULL;
+ m_processedEvent = NULL;
m_sortFunction = NULL;
m_inDoPropertyChanged = 0;
m_inCommitChangesFromEditor = 0;
m_coloursCustomized = 0;
m_frozen = 0;
- m_canvas = NULL;
-
#if wxPG_DOUBLE_BUFFER
m_doubleBuffer = NULL;
#endif
}
if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) )
- m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
+ m_pState->m_dontCenterSplitter = true;
if ( m_windowStyle & wxPG_HIDE_CATEGORIES )
{
m_tlp = NULL;
m_tlpClosed = NULL;
m_tlpClosedTime = 0;
- OnTLPChanging(::wxGetTopLevelParent(this));
// set virtual size to this window size
wxSize wndsize = GetSize();
m_timeCreated = ::wxGetLocalTimeMillis();
- m_canvas = new wxPGCanvas();
- m_canvas->Create(this, 1, wxPoint(0, 0), GetClientSize(),
- wxWANTS_CHARS | wxCLIP_CHILDREN);
- m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM );
+ //m_canvas->Create(this, wxID_ANY, wxPoint(0, 0), GetClientSize(),
+ // wxWANTS_CHARS | wxCLIP_CHILDREN);
+ SetBackgroundStyle( wxBG_STYLE_CUSTOM );
m_iFlags |= wxPG_FL_INITIALIZED;
{
size_t i;
+#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();
+ ReleaseMouse();
// Call with NULL to disconnect event handling
- OnTLPChanging(NULL);
+ if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING )
+ {
+ 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 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;
}
}
bool wxPropertyGrid::Destroy()
{
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
- m_canvas->ReleaseMouse();
+ ReleaseMouse();
return wxScrolledWindow::Destroy();
}
//
// Tooltips disabled
//
- m_canvas->SetToolTip( NULL );
+ wxScrolledWindow::SetToolTip( NULL );
}
#endif
}
wxScrolledWindow::Thaw();
RecalculateVirtualSize();
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- m_canvas->Refresh();
+ Refresh();
#endif
// Force property re-selection
// -----------------------------------------------------------------------
+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 )
{
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__)
10
);
- const wxSize sz = wxSize(60, lineHeight*numLines + 40);
+ 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();
//
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();
void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
{
wxPaintDC dc(this);
+ PrepareDC(dc);
+
+ // Don't paint after destruction has begun
+ if ( !HasInternalFlag(wxPG_FL_INITIALIZED) )
+ return;
+
+ // Find out where the window is scrolled to
+ int vx,vy; // Top left corner of client
+ GetViewStart(&vx,&vy);
+ vy *= wxPG_PIXELS_PER_UNIT;
// Update everything inside the box
wxRect r = GetUpdateRegion().GetBox();
- dc.SetPen(m_colEmptySpace);
- dc.SetBrush(m_colEmptySpace);
- dc.DrawRectangle(r);
+ r.y += vy;
+
+ // FIXME: This is just a workaround for a bug that causes splitters not
+ // to paint when other windows are being dragged over the grid.
+ wxRect fullRect = GetRect();
+ r.x = fullRect.x;
+ r.width = fullRect.width;
+
+ // Repaint this rectangle
+ DrawItems( dc, r.y, r.y + r.height, &r );
+
+ // We assume that the size set when grid is shown
+ // is what is desired.
+ SetInternalFlag(wxPG_FL_GOOD_SIZE_SET);
}
// -----------------------------------------------------------------------
// topy and bottomy are already unscrolled (ie. physical)
//
void wxPropertyGrid::DrawItems( wxDC& dc,
- unsigned int topy,
- unsigned int bottomy,
- const wxRect* clipRect )
-{
- if ( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) return;
+ unsigned int topItemY,
+ unsigned int bottomItemY,
+ const wxRect* drawRect )
+{
+ if ( m_frozen ||
+ m_height < 1 ||
+ bottomItemY < topItemY ||
+ !m_pState )
+ return;
m_pState->EnsureVirtualHeight();
- wxRect tempClipRect;
- if ( !clipRect )
+ wxRect tempDrawRect;
+ if ( !drawRect )
{
- tempClipRect = wxRect(0,topy,m_pState->m_width,bottomy);
- clipRect = &tempClipRect;
+ tempDrawRect = wxRect(0, topItemY,
+ m_pState->m_width,
+ bottomItemY);
+ drawRect = &tempDrawRect;
}
// items added check
{
if ( !m_doubleBuffer )
{
- paintFinishY = clipRect->y;
+ paintFinishY = drawRect->y;
dcPtr = NULL;
}
else
if ( dcPtr )
{
- dc.SetClippingRegion( *clipRect );
- paintFinishY = DoDrawItems( *dcPtr, clipRect, isBuffered );
+ dc.SetClippingRegion( *drawRect );
+ paintFinishY = DoDrawItems( *dcPtr, drawRect, isBuffered );
+ int drawBottomY = drawRect->y + drawRect->height;
+
+ // Clear area beyond last painted property
+ if ( paintFinishY < drawBottomY )
+ {
+ dcPtr->SetPen(m_colEmptySpace);
+ dcPtr->SetBrush(m_colEmptySpace);
+ dcPtr->DrawRectangle(0, paintFinishY,
+ m_width,
+ drawBottomY );
+ }
+
+ dc.DestroyClippingRegion();
}
#if wxPG_DOUBLE_BUFFER
if ( bufferDC )
{
- dc.Blit( clipRect->x, clipRect->y, clipRect->width, clipRect->height,
- bufferDC, 0, 0, wxCOPY );
- dc.DestroyClippingRegion(); // Is this really necessary?
+ dc.Blit( drawRect->x, drawRect->y, drawRect->width,
+ drawRect->height,
+ bufferDC, 0, 0, wxCOPY );
delete bufferDC;
}
#endif
}
-
- // Clear area beyond bottomY?
- if ( paintFinishY < (clipRect->y+clipRect->height) )
+ else
{
+ // Just clear the area
dc.SetPen(m_colEmptySpace);
dc.SetBrush(m_colEmptySpace);
- dc.DrawRectangle( 0, paintFinishY, m_width, (clipRect->y+clipRect->height) );
+ dc.DrawRectangle(*drawRect);
}
}
// -----------------------------------------------------------------------
int wxPropertyGrid::DoDrawItems( wxDC& dc,
- const wxRect* clipRect,
+ const wxRect* drawRect,
bool isBuffered ) const
{
const wxPGProperty* firstItem;
const wxPGProperty* lastItem;
- firstItem = DoGetItemAtY(clipRect->y);
- lastItem = DoGetItemAtY(clipRect->y+clipRect->height-1);
+ firstItem = DoGetItemAtY(drawRect->y);
+ lastItem = DoGetItemAtY(drawRect->y+drawRect->height-1);
if ( !lastItem )
lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
if ( m_frozen || m_height < 1 || firstItem == NULL )
- return clipRect->y;
+ return drawRect->y;
- wxCHECK_MSG( !m_pState->m_itemsAdded, clipRect->y, wxT("no items added") );
+ wxCHECK_MSG( !m_pState->m_itemsAdded, drawRect->y,
+ "no items added" );
wxASSERT( m_pState->m_properties->GetChildCount() );
int lh = m_lineHeight;
int firstItemTopY;
int lastItemBottomY;
- firstItemTopY = clipRect->y;
- lastItemBottomY = clipRect->y + clipRect->height;
+ firstItemTopY = drawRect->y;
+ lastItemBottomY = drawRect->y + drawRect->height;
// Align y coordinates to item boundaries
firstItemTopY -= firstItemTopY % lh;
lastItemBottomY -= 1;
// Entire range outside scrolled, visible area?
- if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || lastItemBottomY <= 0 )
- return clipRect->y;
-
- wxCHECK_MSG( firstItemTopY < lastItemBottomY, clipRect->y, wxT("invalid y values") );
+ if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() ||
+ lastItemBottomY <= 0 )
+ return drawRect->y;
+ wxCHECK_MSG( firstItemTopY < lastItemBottomY,
+ drawRect->y,
+ "invalid y values" );
/*
- wxLogDebug(wxT(" -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"),
+ wxLogDebug(" -> DoDrawItems ( \"%s\" -> \"%s\"
+ "height=%i (ch=%i), drawRect = 0x%lX )",
firstItem->GetLabel().c_str(),
lastItem->GetLabel().c_str(),
(int)(lastItemBottomY - firstItemTopY),
(int)m_height,
- (unsigned long)clipRect );
+ (unsigned long)drawRect );
*/
wxRect r;
//
// With wxPG_DOUBLE_BUFFER, do double buffering
- // - buffer's y = 0, so align cliprect and coordinates to that
+ // - buffer's y = 0, so align drawRect and coordinates to that
//
#if wxPG_DOUBLE_BUFFER
if ( isBuffered )
{
- xRelMod = clipRect->x;
- yRelMod = clipRect->y;
+ xRelMod = drawRect->x;
+ yRelMod = drawRect->y;
//
- // clipRect conversion
- cr2 = *clipRect;
+ // drawRect conversion
+ cr2 = *drawRect;
cr2.x -= xRelMod;
cr2.y -= yRelMod;
- clipRect = &cr2;
+ drawRect = &cr2;
firstItemTopY -= yRelMod;
lastItemBottomY -= yRelMod;
}
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;
}
#endif
- return y + yRelMod;
+ return y;
}
// -----------------------------------------------------------------------
wxRect r = GetPropertyRect(p1, p2);
if ( r.width > 0 )
{
- m_canvas->RefreshRect(r);
+ RefreshRect(r);
}
}
{
PrepareAfterItemsAdded();
- wxWindow::Refresh(false);
- if ( m_canvas )
- // TODO: Coordinate translation
- m_canvas->Refresh(false, rect);
+ wxWindow::Refresh(false, rect);
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
// I think this really helps only GTK+1.2
{
//
// Just in case, fully re-center splitter
- if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) )
- pNewState->m_fSplitterX = -1.0;
+ //if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) )
+ // pNewState->m_fSplitterX = -1.0;
- pNewState->OnClientWidthChange( pgWidth, pgWidth - pNewState->m_width );
+ pNewState->OnClientWidthChange(pgWidth,
+ pgWidth - pNewState->m_width);
}
m_propHover = NULL;
// Call to SetSplitterPosition will always disable splitter auto-centering
// if parent window is shown.
-void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int splitterIndex, bool allPages )
+void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh,
+ int splitterIndex,
+ bool allPages )
{
if ( ( newxpos < wxPG_DRAG_MARGIN ) )
return;
void wxPropertyGrid::CenterSplitter( bool enableAutoCentering )
{
- SetSplitterPosition( m_width/2, true );
- if ( enableAutoCentering && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
- m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
+ SetSplitterPosition( m_width/2 );
+ if ( enableAutoCentering && HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
+ m_pState->m_dontCenterSplitter = false;
}
// -----------------------------------------------------------------------
m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior;
- if ( pendingValue.GetType() == wxPG_VARIANT_TYPE_LIST )
+ //
+ // Variant list a special value that cannot be validated
+ // by normal means.
+ if ( pendingValue.GetType() != wxPG_VARIANT_TYPE_LIST )
{
if ( !p->ValidateValue(pendingValue, m_validationInfo) )
return false;
// Somehow, event is handled after property has been deselected.
// Possibly, but very rare.
- if ( !selected || selected->HasFlag(wxPG_PROP_BEING_DELETED) )
+ 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 )
wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const
{
int itemy = p->GetY2(m_lineHeight);
- int vy = 0;
int splitterX = m_pState->DoGetSplitterPosition(column-1);
int colEnd = splitterX + m_pState->m_colWidths[column];
int imageOffset = 0;
+ int vx, vy; // Top left corner of client
+ GetViewStart(&vx, &vy);
+ vy *= wxPG_PIXELS_PER_UNIT;
+
// TODO: If custom image detection changes from current, change this.
if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
{
if ( type == wxCURSOR_SIZEWE )
cursor = m_cursorSizeWE;
- m_canvas->SetCursor( *cursor );
+ SetCursor( *cursor );
m_curcursor = type;
}
wxWindow* parent = focus->GetParent();
while ( parent )
{
- if ( parent == m_canvas )
+ if ( parent == this )
{
SetFocusOnCanvas();
break;
m_iFlags &= ~wxPG_FL_FIXED_WIDTH_EDITOR;
- wxPGWindowList wndList = editor->CreateControls(this,
- p,
- goodPos,
- grect.GetSize());
+ wxPGWindowList wndList =
+ editor->CreateControls(this,
+ p,
+ goodPos,
+ grect.GetSize());
m_wndEditor = wndList.m_primary;
m_wndEditor2 = wndList.m_secondary;
// Essentially, primaryCtrl == m_wndEditor
//
- // NOTE: It is allowed for m_wndEditor to be NULL - in this case
- // value is drawn as normal, and m_wndEditor2 is assumed
- // to be a right-aligned button that triggers a separate editorCtrl
- // window.
+ // NOTE: It is allowed for m_wndEditor to be NULL - in this
+ // case value is drawn as normal, and m_wndEditor2 is
+ // assumed to be a right-aligned button that triggers
+ // a separate editorCtrl window.
if ( m_wndEditor )
{
wxASSERT_MSG( m_wndEditor->GetParent() == GetPanel(),
- wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") );
+ "CreateControls must use result of "
+ "wxPropertyGrid::GetPanel() as parent "
+ "of controls." );
// Set validator, if any
#if wxUSE_VALIDATORS
// If it has modified status, use bold font
// (must be done before capturing m_ctrlXAdjust)
- if ( (p->m_flags & wxPG_PROP_MODIFIED) && (m_windowStyle & wxPG_BOLD_MODIFIED) )
+ if ( (p->m_flags & wxPG_PROP_MODIFIED) &&
+ (m_windowStyle & wxPG_BOLD_MODIFIED) )
SetCurControlBoldFont();
- //
- // Fix TextCtrl indentation
- #if defined(__WXMSW__) && !defined(__WXWINCE__)
- wxTextCtrl* tc = NULL;
- if ( primaryCtrl->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
- tc = ((wxOwnerDrawnComboBox*)primaryCtrl)->GetTextCtrl();
- else
- tc = wxDynamicCast(primaryCtrl, wxTextCtrl);
- if ( tc )
- ::SendMessage(GetHwndOf(tc), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
- #endif
-
// Store x relative to splitter (we'll need it).
m_ctrlXAdjust = m_wndEditor->GetPosition().x - splitterX;
if ( m_wndEditor2 )
{
wxASSERT_MSG( m_wndEditor2->GetParent() == GetPanel(),
- wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") );
+ "CreateControls must use result of "
+ "wxPropertyGrid::GetPanel() as parent "
+ "of controls." );
// Get proper id for wndSecondary
m_wndSecId = m_wndEditor2->GetId();
}
else
{
- // Make sure focus is in grid canvas (important for wxGTK, at least)
+ // Make sure focus is in grid canvas (important for wxGTK,
+ // at least)
SetFocusOnCanvas();
}
}
// Store dont-center-splitter flag 'cause we need to temporarily set it
- wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
- m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
+ bool prevDontCenterSplitter = m_pState->m_dontCenterSplitter;
+ m_pState->m_dontCenterSplitter = true;
bool res = m_pState->DoCollapse(pwc);
SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
RecalculateVirtualSize();
-
- // Redraw etc. only if collapsed was visible.
- if (pwc->IsVisible() &&
- !m_frozen &&
- ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) )
- {
- // When item is collapsed so that scrollbar would move,
- // graphics mess is about (unless we redraw everything).
- Refresh();
- }
+ Refresh();
}
- // Clear dont-center-splitter flag if it wasn't set
- m_iFlags = (m_iFlags & ~wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
+ m_pState->m_dontCenterSplitter = prevDontCenterSplitter;
return res;
}
wxPGProperty* pwc = (wxPGProperty*)p;
// Store dont-center-splitter flag 'cause we need to temporarily set it
- wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
- m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
+ bool prevDontCenterSplitter = m_pState->m_dontCenterSplitter;
+ m_pState->m_dontCenterSplitter = true;
bool res = m_pState->DoExpand(pwc);
SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
RecalculateVirtualSize();
-
- // Redraw etc. only if expanded was visible.
- if ( pwc->IsVisible() && !m_frozen &&
- ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
- )
- {
- // Redraw
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- Refresh();
- #else
- DrawItems(pwc, NULL);
- #endif
- }
+ Refresh();
}
- // Clear dont-center-splitter flag if it wasn't set
- m_iFlags = (m_iFlags & ~wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
+ m_pState->m_dontCenterSplitter = prevDontCenterSplitter;
return res;
}
m_width = width;
m_height = height;
- m_canvas->SetSize( x, y );
-
m_pState->CheckColumnWidths();
if ( GetSelection() )
return;
int width, height;
- GetClientSize(&width,&height);
+ GetClientSize(&width, &height);
m_width = width;
m_height = height;
void wxPropertyGrid::SetFocusOnCanvas()
{
- m_canvas->SetFocusIgnoringChildren();
+ SetFocusIgnoringChildren();
m_editorFocused = 0;
}
evt.SetEventObject(m_eventObject);
evt.SetProperty(p);
evt.SetColumn(column);
- if ( pValue )
+ if ( eventType == wxEVT_PG_CHANGING )
{
+ wxASSERT( pValue );
evt.SetCanVeto(true);
- evt.SetupValidationInfo();
m_validationInfo.m_pValue = pValue;
+ evt.SetupValidationInfo();
}
- else if ( !(selFlags & wxPG_SEL_NOVALIDATE) )
+ else
{
- evt.SetCanVeto(true);
- }
+ if ( p )
+ evt.SetPropertyValue(p->GetValue());
- wxEvtHandler* evtHandler = m_eventObject->GetEventHandler();
+ if ( !(selFlags & wxPG_SEL_NOVALIDATE) )
+ evt.SetCanVeto(true);
+ }
- evtHandler->ProcessEvent(evt);
+ m_processedEvent = &evt;
+ m_eventObject->HandleWindowEvent(evt);
+ m_processedEvent = NULL;
return evt.WasVetoed();
}
if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
{
- m_canvas->CaptureMouse();
+ CaptureMouse();
m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
}
m_draggedSplitter = splitterHit;
m_dragOffset = splitterHitOffset;
- wxClientDC dc(m_canvas);
-
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
// Fixes button disappearance bug
if ( m_wndEditor2 )
{
if ( tipString.length() )
{
- m_canvas->SetToolTip(tipString);
+ wxScrolledWindow::SetToolTip(tipString);
}
else
{
#if wxPG_ALLOW_EMPTY_TOOLTIPS
- m_canvas->SetToolTip( m_emptyString );
+ wxScrolledWindow::SetToolTip( m_emptyString );
#else
- m_canvas->SetToolTip( NULL );
+ wxScrolledWindow::SetToolTip( NULL );
#endif
}
}
// -----------------------------------------------------------------------
// Return false if should be skipped
-bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event )
+bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y,
+ wxMouseEvent &event )
{
// Safety check (needed because mouse capturing may
// otherwise freeze the control)
if ( m_dragStatus > 0 && !event.Dragging() )
{
- HandleMouseUp(x,y,event);
+ HandleMouseUp(x, y, event);
}
wxPropertyGridPageState* state = m_pState;
if ( newSplitterX != splitterX )
{
// Move everything
- SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
- state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false );
+ state->m_dontCenterSplitter = true;
+ state->DoSetSplitterPosition(newSplitterX,
+ m_draggedSplitter,
+ false);
state->m_fSplitterX = (float) newSplitterX;
if ( GetSelection() )
//
if ( m_windowStyle & wxPG_TOOLTIPS )
{
- wxToolTip* tooltip = m_canvas->GetToolTip();
+ wxToolTip* tooltip = GetToolTip();
if ( m_propHover != prevHover || prevSide != m_mouseSide )
{
space = m_width - splitterX;
if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
- space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
+ space -= wxPG_CUSTOM_IMAGE_WIDTH +
+ wxCC_CUSTOM_IMAGE_MARGIN1 +
+ wxCC_CUSTOM_IMAGE_MARGIN2;
}
if ( space )
if ( tooltip )
{
#if wxPG_ALLOW_EMPTY_TOOLTIPS
- m_canvas->SetToolTip( m_emptyString );
+ SetToolTip( m_emptyString );
#else
- m_canvas->SetToolTip( NULL );
+ wxScrolledWindow::SetToolTip( NULL );
#endif
}
}
if ( tooltip )
{
#if wxPG_ALLOW_EMPTY_TOOLTIPS
- m_canvas->SetToolTip( m_emptyString );
+ SetToolTip( m_emptyString );
#else
- m_canvas->SetToolTip( NULL );
+ wxScrolledWindow::SetToolTip( NULL );
#endif
}
}
// hovering on splitter
- // NB: Condition disabled since MouseLeave event (from the editor control) cannot be
- // reliably detected.
+ // NB: Condition disabled since MouseLeave event (from the
+ // editor control) cannot be reliably detected.
//if ( m_curcursor != wxCURSOR_SIZEWE )
CustomSetCursor( wxCURSOR_SIZEWE, true );
//
// Multi select by dragging
//
- if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION &&
+ if ( (GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) &&
event.LeftIsDown() &&
m_propHover &&
GetSelection() &&
+ columnHit != 1 &&
!state->DoIsPropertySelected(m_propHover) )
{
- DoAddToSelection(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;
//splitterX = x;
// Disable splitter auto-centering
- m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
+ state->m_dontCenterSplitter = true;
// This is necessary to return cursor
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
{
- m_canvas->ReleaseMouse();
+ ReleaseMouse();
m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
}
{
int splitterX = GetSplitterPosition();
- //int ux, uy;
- //CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
- int ux = event.m_x;
- int uy = event.m_y;
+ int ux, uy;
+ CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
wxWindow* wnd = GetEditorControl();
// -----------------------------------------------------------------------
-void wxPropertyGrid::OnMouseMoveBottom( wxMouseEvent& WXUNUSED(event) )
-{
- // Called when mouse moves in the empty space below the properties.
- CustomSetCursor( wxCURSOR_ARROW );
-}
-
-// -----------------------------------------------------------------------
-
void wxPropertyGrid::OnMouseUp( wxMouseEvent &event )
{
int x, y;
else if ( event.Leaving() )
{
// Without this, wxSpinCtrl editor will sometimes have wrong cursor
- m_canvas->SetCursor( wxNullCursor );
+ SetCursor( wxNullCursor );
// Get real cursor position
wxPoint pt = ScreenToClient(::wxGetMousePosition());
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 )
//
// Check if top-level parent has changed
- wxWindow* tlp = ::wxGetTopLevelParent(this);
- if ( tlp != m_tlp )
+ if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING )
{
- OnTLPChanging(tlp);
+ wxWindow* tlp = ::wxGetTopLevelParent(this);
+ if ( tlp != m_tlp )
+ OnTLPChanging(tlp);
}
}
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;
+ }
+ }
+ }
}
// -----------------------------------------------------------------------