// Created: 2004-09-25
// RCS-ID: $Id$
// Copyright: (c) Jaakko Salli
-// Licence: wxWindows license
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/timer.h"
#include "wx/dcbuffer.h"
+#include "wx/scopeguard.h"
// Two pics for the expand / collapse buttons.
// Files are not supplied with this project (since it is
public:
wxPGGlobalVarsClassManager() {}
virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; }
- virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; }
+ virtual void OnExit() { wxDELETE(wxPGGlobalVars); }
};
IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
void wxPGInitResourceModule()
{
wxModule* module = new wxPGGlobalVarsClassManager;
- module->Init();
wxModule::RegisterModule(module);
+ wxModule::InitializeModules();
}
wxPGGlobalVarsClass* wxPGGlobalVars = NULL;
delete ((wxPGEditor*)vt_it->second);
}
+ // Make sure the global pointers have been reset
+ wxASSERT(wxPG_EDITOR(TextCtrl) == NULL);
+ wxASSERT(wxPG_EDITOR(ChoiceAndButton) == NULL);
+
delete wxPGProperty::sm_wxPG_LABEL;
}
// wxPropertyGrid
// -----------------------------------------------------------------------
-IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxScrolledWindow)
+IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxControl)
-BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
+BEGIN_EVENT_TABLE(wxPropertyGrid, wxControl)
EVT_IDLE(wxPropertyGrid::OnIdle)
EVT_PAINT(wxPropertyGrid::OnPaint)
EVT_SIZE(wxPropertyGrid::OnResize)
// -----------------------------------------------------------------------
wxPropertyGrid::wxPropertyGrid()
- : wxScrolledWindow()
+ : wxControl(), wxScrollHelper(this)
{
Init1();
}
const wxSize& size,
long style,
const wxString& name )
- : wxScrolledWindow()
+ : wxControl(), wxScrollHelper(this)
{
Init1();
Create(parent,id,pos,size,style,name);
style &= ~(wxTAB_TRAVERSAL);
style |= wxWANTS_CHARS;
- wxScrolledWindow::Create(parent,id,pos,size,style,name);
+ wxControl::Create(parent, id, pos, size,
+ style | wxScrolledWindowStyle,
+ wxDefaultValidator,
+ name);
Init2();
if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
wxPropertyGrid::RegisterDefaultEditors();
+ m_validatingEditor = 0;
m_iFlags = 0;
m_pState = NULL;
m_wndEditor = m_wndEditor2 = NULL;
m_eventObject = this;
m_curFocused = NULL;
m_processedEvent = NULL;
+ m_tlp = NULL;
m_sortFunction = NULL;
- m_inDoPropertyChanged = 0;
- m_inCommitChangesFromEditor = 0;
- m_inDoSelectProperty = 0;
+ m_inDoPropertyChanged = false;
+ m_inCommitChangesFromEditor = false;
+ m_inDoSelectProperty = false;
+ m_inOnValidationFailure = false;
m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT;
m_dragStatus = 0;
m_mouseSide = 16;
m_editorFocused = 0;
- // Must set empty but valid data
- m_unspecifiedAppearance.SetEmptyData();
+ // Set up default unspecified value 'colour'
+ m_unspecifiedAppearance.SetFgCol(*wxLIGHT_GREY);
// Set default keys
AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT );
m_coloursCustomized = 0;
m_frozen = 0;
-#if wxPG_DOUBLE_BUFFER
m_doubleBuffer = NULL;
-#endif
#ifndef wxPG_ICON_WIDTH
m_expandbmp = NULL;
SetBackgroundStyle( wxBG_STYLE_CUSTOM );
// Hook the top-level parent
- m_tlp = NULL;
m_tlpClosed = NULL;
m_tlpClosedTime = 0;
m_timeCreated = ::wxGetLocalTimeMillis();
- //m_canvas->Create(this, wxID_ANY, wxPoint(0, 0), GetClientSize(),
- // wxWANTS_CHARS | wxCLIP_CHILDREN);
- SetBackgroundStyle( wxBG_STYLE_CUSTOM );
-
m_iFlags |= wxPG_FL_INITIALIZED;
m_ncWidth = wndsize.GetWidth();
wxS("Close(false).)") );
}
-#if wxPG_DOUBLE_BUFFER
if ( m_doubleBuffer )
delete m_doubleBuffer;
-#endif
if ( m_iFlags & wxPG_FL_CREATEDSTATE )
delete m_pState;
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
ReleaseMouse();
- return wxScrolledWindow::Destroy();
+ return wxControl::Destroy();
}
// -----------------------------------------------------------------------
//
// Tooltips disabled
//
- wxScrolledWindow::SetToolTip( NULL );
+ SetToolTip( NULL );
}
#endif
}
- wxScrolledWindow::SetWindowStyleFlag ( style );
+ wxControl::SetWindowStyleFlag ( style );
if ( m_iFlags & wxPG_FL_INITIALIZED )
{
{
if ( !m_frozen )
{
- wxScrolledWindow::Freeze();
+ wxControl::Freeze();
}
m_frozen++;
}
if ( !m_frozen )
{
- wxScrolledWindow::Thaw();
+ wxControl::Thaw();
RecalculateVirtualSize();
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
Refresh();
- #endif
// Force property re-selection
// NB: We must copy the selection.
colIndex);
wxWindowID id = tc->GetId();
- tc->Connect(id, wxEVT_COMMAND_TEXT_ENTER,
+ tc->Connect(id, wxEVT_TEXT_ENTER,
wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress),
NULL, this);
tc->Connect(id, wxEVT_KEY_DOWN,
}
else
{
- event.Skip();
+ HandleKeyEvent(event, true);
}
}
}
m_selColumn = 1;
+ int wasFocused = m_iFlags & wxPG_FL_FOCUSED;
DestroyEditorWnd(m_labelEditor);
+
m_labelEditor = NULL;
m_labelEditorProperty = NULL;
+ // Fix focus (needed at least on wxGTK)
+ if ( wasFocused )
+ SetFocusOnCanvas();
+
DrawItem(prop);
}
}
else
{
- #if wxPG_DOUBLE_BUFFER
- delete m_doubleBuffer;
- m_doubleBuffer = NULL;
- #endif
+ wxDELETE(m_doubleBuffer);
}
}
- wxScrolledWindow::SetExtraStyle( exStyle );
+ wxControl::SetExtraStyle( exStyle );
if ( exStyle & wxPG_EX_INIT_NOCAT )
m_pState->InitNonCatMode();
{
OnTLPChanging((wxWindow*)newParent);
- bool res = wxScrolledWindow::Reparent(newParent);
+ bool res = wxControl::Reparent(newParent);
return res;
}
{
int x = 0, y = 0;
- m_captionFont = wxScrolledWindow::GetFont();
+ m_captionFont = wxControl::GetFont();
GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
m_subgroup_extramargin = x + (x/2);
// Must disable active editor.
DoClearSelection();
- bool res = wxScrolledWindow::SetFont( font );
+ bool res = wxControl::SetFont( font );
if ( res && GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant
{
CalculateFontAndBitmapStuff( m_vspacing );
// Control font changer helper.
void wxPropertyGrid::SetCurControlBoldFont()
{
- wxASSERT( m_wndEditor );
- m_wndEditor->SetFont( m_captionFont );
+ wxWindow* editor = GetEditorControl();
+ editor->SetFont( m_captionFont );
}
// -----------------------------------------------------------------------
wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str )
{
- if ( src_str.length() == 0 )
+ if ( src_str.empty() )
{
dst_str = src_str;
return src_str;
wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str )
{
- if ( src_str.length() == 0 )
+ if ( src_str.empty() )
{
dst_str = src_str;
return src_str;
r.x = 0;
r.width = GetClientSize().x;
+ r.y = vy;
+ r.height = GetClientSize().y;
+
// Repaint this rectangle
DrawItems( dc, r.y, r.y + r.height, &r );
void wxPropertyGrid::DrawItems( wxDC& dc,
unsigned int topItemY,
unsigned int bottomItemY,
- const wxRect* drawRect )
+ const wxRect* itemsRect )
{
if ( m_frozen ||
m_height < 1 ||
m_pState->EnsureVirtualHeight();
- wxRect tempDrawRect;
- if ( !drawRect )
+ wxRect tempItemsRect;
+ if ( !itemsRect )
{
- tempDrawRect = wxRect(0, topItemY,
- m_pState->m_width,
- bottomItemY);
- drawRect = &tempDrawRect;
+ tempItemsRect = wxRect(0, topItemY,
+ m_pState->m_width,
+ bottomItemY);
+ itemsRect = &tempItemsRect;
}
+ int vx, vy;
+ GetViewStart(&vx, &vy);
+ vx *= wxPG_PIXELS_PER_UNIT;
+ vy *= wxPG_PIXELS_PER_UNIT;
+
+ // itemRect is in virtual grid space
+ wxRect drawRect(itemsRect->x - vx,
+ itemsRect->y - vy,
+ itemsRect->width,
+ itemsRect->height);
+
// items added check
if ( m_pState->m_itemsAdded ) PrepareAfterItemsAdded();
wxDC* dcPtr = &dc;
bool isBuffered = false;
- #if wxPG_DOUBLE_BUFFER
wxMemoryDC* bufferDC = NULL;
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
{
if ( !m_doubleBuffer )
{
- paintFinishY = drawRect->y;
+ paintFinishY = itemsRect->y;
dcPtr = NULL;
}
else
isBuffered = true;
}
}
- #endif
if ( dcPtr )
{
- dc.SetClippingRegion( *drawRect );
- paintFinishY = DoDrawItems( *dcPtr, drawRect, isBuffered );
- int drawBottomY = drawRect->y + drawRect->height;
+ // paintFinishY and drawBottomY are in buffer/physical space
+ paintFinishY = DoDrawItems( *dcPtr, itemsRect, isBuffered );
+ int drawBottomY = itemsRect->y + itemsRect->height - vy;
// Clear area beyond last painted property
if ( paintFinishY < drawBottomY )
m_width,
drawBottomY );
}
-
- dc.DestroyClippingRegion();
}
- #if wxPG_DOUBLE_BUFFER
if ( bufferDC )
{
- dc.Blit( drawRect->x, drawRect->y, drawRect->width,
- drawRect->height,
+ dc.Blit( drawRect.x, drawRect.y, drawRect.width,
+ drawRect.height,
bufferDC, 0, 0, wxCOPY );
delete bufferDC;
}
- #endif
}
else
{
// Just clear the area
dc.SetPen(m_colEmptySpace);
dc.SetBrush(m_colEmptySpace);
- dc.DrawRectangle(*drawRect);
+ dc.DrawRectangle(drawRect);
}
}
// -----------------------------------------------------------------------
int wxPropertyGrid::DoDrawItems( wxDC& dc,
- const wxRect* drawRect,
+ const wxRect* itemsRect,
bool isBuffered ) const
{
const wxPGProperty* firstItem;
const wxPGProperty* lastItem;
- firstItem = DoGetItemAtY(drawRect->y);
- lastItem = DoGetItemAtY(drawRect->y+drawRect->height-1);
+ firstItem = DoGetItemAtY(itemsRect->y);
+ lastItem = DoGetItemAtY(itemsRect->y+itemsRect->height-1);
if ( !lastItem )
lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
if ( m_frozen || m_height < 1 || firstItem == NULL )
- return drawRect->y;
+ return itemsRect->y;
- wxCHECK_MSG( !m_pState->m_itemsAdded, drawRect->y,
+ wxCHECK_MSG( !m_pState->m_itemsAdded, itemsRect->y,
"no items added" );
wxASSERT( m_pState->m_properties->GetChildCount() );
int firstItemTopY;
int lastItemBottomY;
- firstItemTopY = drawRect->y;
- lastItemBottomY = drawRect->y + drawRect->height;
+ firstItemTopY = itemsRect->y;
+ lastItemBottomY = itemsRect->y + itemsRect->height;
// Align y coordinates to item boundaries
firstItemTopY -= firstItemTopY % lh;
// Entire range outside scrolled, visible area?
if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() ||
lastItemBottomY <= 0 )
- return drawRect->y;
+ return itemsRect->y;
wxCHECK_MSG( firstItemTopY < lastItemBottomY,
- drawRect->y,
+ itemsRect->y,
"invalid y values" );
/*
wxLogDebug(" -> DoDrawItems ( \"%s\" -> \"%s\"
- "height=%i (ch=%i), drawRect = 0x%lX )",
+ "height=%i (ch=%i), itemsRect = 0x%lX )",
firstItem->GetLabel().c_str(),
lastItem->GetLabel().c_str(),
(int)(lastItemBottomY - firstItemTopY),
(int)m_height,
- (unsigned long)drawRect );
+ (unsigned long)&itemsRect );
*/
wxRect r;
int xRelMod = 0;
//
- // With wxPG_DOUBLE_BUFFER, do double buffering
- // - buffer's y = 0, so align drawRect and coordinates to that
+ // For now, do some manual calculation for double buffering
+ // - buffer's y = 0, so align itemsRect and coordinates to that
+ //
+ // TODO: In future use wxAutoBufferedPaintDC (for example)
//
-#if wxPG_DOUBLE_BUFFER
int yRelMod = 0;
wxRect cr2;
if ( isBuffered )
{
- xRelMod = drawRect->x;
- yRelMod = drawRect->y;
+ xRelMod = itemsRect->x;
+ yRelMod = itemsRect->y;
//
- // drawRect conversion
- cr2 = *drawRect;
+ // itemsRect conversion
+ cr2 = *itemsRect;
cr2.x -= xRelMod;
cr2.y -= yRelMod;
- drawRect = &cr2;
+ itemsRect = &cr2;
firstItemTopY -= yRelMod;
lastItemBottomY -= yRelMod;
}
-#else
- wxUnusedVar(isBuffered);
-#endif
int x = m_marginWidth - xRelMod;
const wxPropertyGridPageState* state = m_pState;
const wxArrayInt& colWidths = state->m_colWidths;
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- bool wasSelectedPainted = false;
-#endif
-
// TODO: Only render columns that are within clipping region.
dc.SetFont(normalFont);
}
else
{
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- if ( p == firstSelected )
- wasSelectedPainted = true;
-#endif
-
renderFlags |= wxPGCellRenderer::Selected;
if ( !p->IsCategory() )
y += rowHeight;
}
- // Refresh editor controls (seems not needed on msw)
- // NOTE: This code is mandatory for GTK!
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- if ( wasSelectedPainted )
- {
- if ( m_wndEditor )
- m_wndEditor->Refresh();
- if ( m_wndEditor2 )
- m_wndEditor2->Refresh();
- }
-#endif
-
return y;
}
//
// Return rect which encloses the given property range
+ // (in logical grid coordinates)
+ //
int visTop = p1->GetY();
int visBottom;
wxRect r = GetPropertyRect(p1, p2);
if ( r.width > 0 )
{
+ // Convert rectangle from logical grid coordinates to physical ones
+ int vx, vy;
+ GetViewStart(&vx, &vy);
+ vx *= wxPG_PIXELS_PER_UNIT;
+ vy *= wxPG_PIXELS_PER_UNIT;
+ r.x -= vx;
+ r.y -= vy;
RefreshRect(r);
}
}
void wxPropertyGrid::RefreshProperty( wxPGProperty* p )
{
- if ( m_pState->DoIsPropertySelected(p) )
+ if ( m_pState->DoIsPropertySelected(p) || p->IsChildSelected(true) )
{
// NB: We must copy the selection.
wxArrayPGProperty selection = m_pState->m_selection;
wxWindow::Refresh(false, rect);
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+#if wxPG_REFRESH_CONTROLS
// I think this really helps only GTK+1.2
if ( m_wndEditor ) m_wndEditor->Refresh();
if ( m_wndEditor2 ) m_wndEditor2->Refresh();
(m_iFlags & wxPG_FL_INITIALIZED) &&
selected )
{
- m_inCommitChangesFromEditor = 1;
+ m_inCommitChangesFromEditor = true;
wxVariant variant(selected->GetValueRef());
bool valueIsPending = false;
EditorsValueWasNotModified();
}
- bool res = true;
+ m_inCommitChangesFromEditor = false;
- m_inCommitChangesFromEditor = 0;
+ bool res = true;
if ( validationFailure && !forceSuccess )
{
//
m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior;
+ m_validationInfo.m_isFailing = true;
//
// Variant list a special value that cannot be validated
if ( changedProperty == GetSelection() )
{
wxWindow* editor = GetEditorControl();
- wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) );
+ wxASSERT( wxDynamicCast(editor, wxTextCtrl) );
evtChangingValue = wxStaticCast(editor, wxTextCtrl)->GetValue();
}
else
pendingValue = value;
}
+ m_validationInfo.m_isFailing = false;
+
return true;
}
// -----------------------------------------------------------------------
+#if wxUSE_STATUSBAR
+wxStatusBar* wxPropertyGrid::GetStatusBar()
+{
+ wxWindow* topWnd = ::wxGetTopLevelParent(this);
+ if ( wxDynamicCast(topWnd, wxFrame) )
+ {
+ wxFrame* pFrame = wxStaticCast(topWnd, wxFrame);
+ if ( pFrame )
+ return pFrame->GetStatusBar();
+ }
+ return NULL;
+}
+#endif
+
+// -----------------------------------------------------------------------
+
void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), const wxString& msg )
{
- if ( !msg.length() )
+ if ( msg.empty() )
return;
#if wxUSE_STATUSBAR
if ( !wxPGGlobalVars->m_offline )
{
- wxWindow* topWnd = ::wxGetTopLevelParent(this);
- if ( topWnd )
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
{
- wxFrame* pFrame = wxDynamicCast(topWnd, wxFrame);
- if ( pFrame )
- {
- wxStatusBar* pStatusBar = pFrame->GetStatusBar();
- if ( pStatusBar )
- {
- pStatusBar->SetStatusText(msg);
- return;
- }
- }
+ pStatusBar->SetStatusText(msg);
+ return;
}
}
#endif
- ::wxMessageBox(msg, wxT("Property Error"));
+ ::wxMessageBox(msg, _("Property Error"));
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoHidePropertyError( wxPGProperty* WXUNUSED(property) )
+{
+#if wxUSE_STATUSBAR
+ if ( !wxPGGlobalVars->m_offline )
+ {
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
+ {
+ pStatusBar->SetStatusText(wxEmptyString);
+ return;
+ }
+ }
+#endif
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property,
wxVariant& invalidValue )
{
+ if ( m_inOnValidationFailure )
+ return true;
+
+ m_inOnValidationFailure = true;
+ wxON_BLOCK_EXIT_SET(m_inOnValidationFailure, false);
+
wxWindow* editor = GetEditorControl();
+ int vfb = m_validationInfo.m_failureBehavior;
+
+ if ( m_inDoSelectProperty )
+ {
+ // When property selection is being changed, do not display any
+ // messages, if some were already shown for this property.
+ if ( property->HasFlag(wxPG_PROP_INVALID_VALUE) )
+ {
+ m_validationInfo.m_failureBehavior =
+ vfb & ~(wxPG_VFB_SHOW_MESSAGE |
+ wxPG_VFB_SHOW_MESSAGEBOX |
+ wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR);
+ }
+ }
// First call property's handler
property->OnValidationFailure(invalidValue);
//
// For non-wxTextCtrl editors, we do need to revert the value
- if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) &&
+ if ( !wxDynamicCast(editor, wxTextCtrl) &&
property == GetSelection() )
{
property->GetEditorClass()->UpdateControl(property, editor);
}
}
- if ( vfb & wxPG_VFB_SHOW_MESSAGE )
+ if ( vfb & (wxPG_VFB_SHOW_MESSAGE |
+ wxPG_VFB_SHOW_MESSAGEBOX |
+ wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR) )
{
wxString msg = m_validationInfo.m_failureMessage;
- if ( !msg.length() )
- msg = wxT("You have entered invalid value. Press ESC to cancel editing.");
+ if ( msg.empty() )
+ msg = _("You have entered invalid value. Press ESC to cancel editing.");
+
+ #if wxUSE_STATUSBAR
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR )
+ {
+ if ( !wxPGGlobalVars->m_offline )
+ {
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
+ pStatusBar->SetStatusText(msg);
+ }
+ }
+ #endif
+
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE )
+ DoShowPropertyError(property, msg);
- DoShowPropertyError(property, msg);
+ if ( vfb & wxPG_VFB_SHOW_MESSAGEBOX )
+ ::wxMessageBox(msg, _("Property Error"));
}
return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true;
DrawItemAndChildren(property);
}
}
+
+#if wxUSE_STATUSBAR
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR )
+ {
+ if ( !wxPGGlobalVars->m_offline )
+ {
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
+ pStatusBar->SetStatusText(wxEmptyString);
+ }
+ }
+#endif
+
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE )
+ {
+ DoHidePropertyError(property);
+ }
+
+ m_validationInfo.m_isFailing = false;
}
// -----------------------------------------------------------------------
if ( m_inDoPropertyChanged )
return true;
- wxWindow* editor = GetEditorControl();
+ m_inDoPropertyChanged = true;
+ wxON_BLOCK_EXIT_SET(m_inDoPropertyChanged, false);
+
wxPGProperty* selected = GetSelection();
m_pState->m_anyModified = 1;
- m_inDoPropertyChanged = 1;
+ // If property's value is being changed, assume it is valid
+ OnValidationFailureReset(selected);
// Maybe need to update control
wxASSERT( m_chgInfo_changedProperty != NULL );
changedProperty->SetValue(value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER);
+ // NB: Call GetEditorControl() as late as possible, because OnSetValue()
+ // and perhaps other user-defined virtual functions may change it.
+ wxWindow* editor = GetEditorControl();
+
// Set as Modified (not if dragging just began)
if ( !(p->m_flags & wxPG_PROP_MODIFIED) )
{
}
else
{
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+#if wxPG_REFRESH_CONTROLS
if ( m_wndEditor ) m_wndEditor->Refresh();
if ( m_wndEditor2 ) m_wndEditor2->Refresh();
#endif
SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL );
- m_inDoPropertyChanged = 0;
-
return true;
}
// Runs wxValidator for the selected property
bool wxPropertyGrid::DoEditorValidate()
{
+#if wxUSE_VALIDATORS
+ wxRecursionGuard guard(m_validatingEditor);
+ if ( guard.IsInside() )
+ return false;
+
+ wxPGProperty* selected = GetSelection();
+ if ( selected )
+ {
+ wxWindow* wnd = GetEditorControl();
+
+ wxValidator* validator = selected->GetValidator();
+ if ( validator && wnd )
+ {
+ validator->SetWindow(wnd);
+ if ( !validator->Validate(this) )
+ return false;
+ }
+ }
+#endif
return true;
}
// -----------------------------------------------------------------------
-void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
+bool wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
{
+ //
+ // NB: We should return true if the event was recognized as
+ // a dedicated wxPropertyGrid event, and as such was
+ // either properly handled or ignored.
+ //
+
// It is possible that this handler receives event even before
// the control has been properly initialized. Let's skip the
// event handling in that case.
if ( !m_pState )
- return;
+ return false;
+
+ // Don't care about the event if it originated from the
+ // 'label editor'. In this function we only care about the
+ // property value editor.
+ if ( m_labelEditor && event.GetId() == m_labelEditor->GetId() )
+ {
+ event.Skip();
+ return true;
+ }
wxPGProperty* selected = GetSelection();
// Possibly, but very rare.
if ( !selected ||
selected->HasFlag(wxPG_PROP_BEING_DELETED) ||
+ m_inOnValidationFailure ||
// Also don't handle editor event if wxEVT_PG_CHANGED or
// similar is currently doing something (showing a
// message box, for instance).
m_processedEvent )
- return;
+ return true;
if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT )
- return;
+ return true;
wxVariant pendingValue(selected->GetValueRef());
wxWindow* wnd = GetEditorControl();
m_chgInfo_changedProperty = NULL;
- m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED|wxPG_FL_VALUE_CHANGE_IN_EVENT);
+ m_iFlags &= ~wxPG_FL_VALUE_CHANGE_IN_EVENT;
//
// Filter out excess wxTextCtrl modified events
- if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd )
+ if ( event.GetEventType() == wxEVT_TEXT && wnd )
{
- if ( wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
+ if ( wxDynamicCast(wnd, wxTextCtrl) )
{
wxTextCtrl* tc = (wxTextCtrl*) wnd;
wxString newTcValue = tc->GetValue();
if ( m_prevTcValue == newTcValue )
- return;
+ return true;
m_prevTcValue = newTcValue;
}
- else if ( wnd->IsKindOf(CLASSINFO(wxComboCtrl)) )
+ else if ( wxDynamicCast(wnd, wxComboCtrl) )
{
+ // In some cases we might stumble unintentionally on
+ // wxComboCtrl's embedded wxTextCtrl's events. Let's
+ // avoid them.
+ if ( wxDynamicCast(editorWnd, wxTextCtrl) )
+ return false;
+
wxComboCtrl* cc = (wxComboCtrl*) wnd;
wxString newTcValue = cc->GetTextCtrl()->GetValue();
if ( m_prevTcValue == newTcValue )
- return;
+ return true;
m_prevTcValue = newTcValue;
}
}
bool validationFailure = false;
bool buttonWasHandled = false;
+ bool result = false;
//
// Try common button handling
- if ( m_wndEditor2 && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
+ if ( m_wndEditor2 && event.GetEventType() == wxEVT_BUTTON )
{
wxPGEditorDialogAdapter* adapter = selected->GetEditorDialog();
{
buttonWasHandled = true;
// Store as res2, as previously (and still currently alternatively)
- // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED
+ // dialogs can be shown by handling wxEVT_BUTTON
// in wxPGProperty::OnEvent().
adapter->ShowDialog( this, selected );
delete adapter;
if ( editor->OnEvent( this, selected, editorWnd, event ) )
{
+ result = true;
+
// If changes, validate them
if ( DoEditorValidate() )
{
selected,
wnd ) )
valueIsPending = true;
+
+ // Mark value always as pending if validation is currently
+ // failing and value was not unspecified
+ if ( !valueIsPending &&
+ !pendingValue.IsNull() &&
+ m_validationInfo.m_isFailing )
+ valueIsPending = true;
}
else
{
// Regardless of editor type, unfocus editor on
// text-editing related enter press.
- if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+ if ( event.GetEventType() == wxEVT_TEXT_ENTER )
{
SetFocusOnCanvas();
}
// Regardless of editor type, unfocus editor on
// text-editing related enter press.
- if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+ if ( event.GetEventType() == wxEVT_TEXT_ENTER )
{
SetFocusOnCanvas();
}
// Let unhandled button click events go to the parent
- if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
+ if ( !buttonWasHandled && event.GetEventType() == wxEVT_BUTTON )
{
- wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
+ result = true;
+ wxCommandEvent evt(wxEVT_BUTTON,GetId());
GetEventHandler()->AddPendingEvent(evt);
}
}
ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
+
+ return result;
}
// -----------------------------------------------------------------------
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 ( column == 1 )
+ {
+ // TODO: If custom image detection changes from current, change this.
+ if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
+ {
+ //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
+ int iw = p->OnMeasureImage().x;
+ if ( iw < 1 )
+ iw = wxPG_CUSTOM_IMAGE_WIDTH;
+ imageOffset = p->GetImageOffset(iw);
+ }
+ }
+ else if ( column == 0 )
{
- //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
- int iw = p->OnMeasureImage().x;
- if ( iw < 1 )
- iw = wxPG_CUSTOM_IMAGE_WIDTH;
- imageOffset = p->GetImageOffset(iw);
+ splitterX += (p->m_depth - 1) * m_subgroup_extramargin;
}
return wxRect
m_propGrid->HandleCustomEditorEvent(event);
+ //
+ // NB: We should return true if the event was recognized as
+ // a dedicated wxPropertyGrid event, and as such was
+ // either properly handled or ignored.
+ //
+ if ( m_propGrid->IsMainButtonEvent(event) )
+ return true;
+
+ //
+ // NB: On wxMSW, a wxTextCtrl with wxTE_PROCESS_ENTER
+ // may beep annoyingly if that event is skipped
+ // and passed to parent event handler.
+ if ( event.GetEventType() == wxEVT_TEXT_ENTER )
+ return true;
+
return wxEvtHandler::ProcessEvent(event);
}
// Return focus back to canvas from children (this is required at least for
// GTK+, which, unlike Windows, clears focus when control is destroyed
// instead of moving it to closest parent).
- wxWindow* focus = wxWindow::FindFocus();
- if ( focus )
- {
- wxWindow* parent = focus->GetParent();
- while ( parent )
- {
- if ( parent == this )
- {
- SetFocusOnCanvas();
- break;
- }
- parent = parent->GetParent();
- }
- }
+ SetFocusOnCanvas();
// Do not free editors immediately if processing events
if ( m_wndEditor2 )
if ( m_inDoSelectProperty )
return true;
- m_inDoSelectProperty = 1;
+ m_inDoSelectProperty = true;
+ wxON_BLOCK_EXIT_SET(m_inDoSelectProperty, false);
if ( !m_pState )
- {
- m_inDoSelectProperty = 0;
return false;
- }
wxArrayPGProperty prevSelection = m_pState->m_selection;
wxPGProperty* prevFirstSel;
wxPrintf( "P = NULL\n" );
*/
+ wxWindow* primaryCtrl = NULL;
+
// If we are frozen, then just set the values.
if ( m_frozen )
{
}
}
- m_inDoSelectProperty = 0;
return true;
}
// First, deactivate previous
if ( prevFirstSel )
{
- OnValidationFailureReset(prevFirstSel);
-
// Must double-check if this is an selected in case of forceswitch
if ( p != prevFirstSel )
{
// Validation has failed, so we can't exit the previous editor
//::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."),
// _("Invalid Value"),wxOK|wxICON_ERROR);
- m_inDoSelectProperty = 0;
return false;
}
}
+ // This should be called after CommitChangesFromEditor(), so that
+ // OnValidationFailure() still has information on property's
+ // validation state.
+ OnValidationFailureReset(prevFirstSel);
+
FreeEditors();
m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
int splitterX = GetSplitterPosition();
m_editorFocused = 0;
m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
- if ( p != prevFirstSel )
- m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
wxASSERT( m_wndEditor == NULL );
m_wndEditor = wndList.m_primary;
m_wndEditor2 = wndList.m_secondary;
- wxWindow* primaryCtrl = GetEditorControl();
+ primaryCtrl = GetEditorControl();
//
// Essentially, primaryCtrl == m_wndEditor
ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
}
-#if wxUSE_STATUSBAR
+ const wxString* pHelpString = NULL;
- //
- // Show help text in status bar.
- // (if found and grid not embedded in manager with help box and
- // style wxPG_EX_HELP_AS_TOOLTIPS is not used).
- //
+ if ( p )
+ pHelpString = &p->GetHelpString();
if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) )
{
- wxStatusBar* statusbar = NULL;
- if ( !(m_iFlags & wxPG_FL_NOSTATUSBARHELP) )
- {
- wxFrame* frame = wxDynamicCast(::wxGetTopLevelParent(this),wxFrame);
- if ( frame )
- statusbar = frame->GetStatusBar();
- }
+#if wxUSE_STATUSBAR
+ //
+ // Show help text in status bar.
+ // (if found and grid not embedded in manager with help box and
+ // style wxPG_EX_HELP_AS_TOOLTIPS is not used).
+ //
+ wxStatusBar* statusbar = GetStatusBar();
if ( statusbar )
{
- const wxString* pHelpString = (const wxString*) NULL;
-
- if ( p )
+ if ( pHelpString && !pHelpString->empty() )
{
- pHelpString = &p->GetHelpString();
- if ( pHelpString->length() )
- {
- // Set help box text.
- statusbar->SetStatusText( *pHelpString );
- m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
- }
+ // Set help box text.
+ statusbar->SetStatusText( *pHelpString );
+ m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
}
-
- if ( (!pHelpString || !pHelpString->length()) &&
- (m_iFlags & wxPG_FL_STRING_IN_STATUSBAR) )
+ else if ( m_iFlags & wxPG_FL_STRING_IN_STATUSBAR )
{
// Clear help box - but only if it was written
// by us at previous time.
m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR);
}
}
+#endif
}
+ else
+ {
+#if wxPG_SUPPORT_TOOLTIPS
+ //
+ // Show help as a tool tip on the editor control.
+ //
+ if ( pHelpString && !pHelpString->empty() &&
+ primaryCtrl )
+ {
+ primaryCtrl->SetToolTip(*pHelpString);
+ }
#endif
-
- m_inDoSelectProperty = 0;
+ }
// call wx event handler (here so that it also occurs on deselection)
if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) )
SetScrollbars( wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT,
xAmount, yAmount, xPos, yPos, true );
+ // This may be needed in addition to calling SetScrollbars()
+ // when class inherits from wxScrollHelper instead of
+ // actual wxScrolled<T>.
+ AdjustScrollbars();
+
// Must re-get size now
GetClientSize(&width,&height);
if ( !HasVirtualWidth() )
{
m_pState->SetVirtualWidth(width);
- x = width;
}
m_width = width;
m_width = width;
m_height = height;
-#if wxPG_DOUBLE_BUFFER
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
{
int dblh = (m_lineHeight*2);
}
}
-#endif
-
m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true );
m_ncWidth = event.GetSize().x;
void wxPropertyGrid::SetFocusOnCanvas()
{
- SetFocusIgnoringChildren();
+ // To prevent wxPropertyGrid from stealing focus from other controls,
+ // only move focus to the grid if it was already in one if its child
+ // controls.
+ wxWindow* focus = wxWindow::FindFocus();
+ if ( focus )
+ {
+ wxWindow* parent = focus->GetParent();
+ while ( parent )
+ {
+ if ( parent == this )
+ {
+ SetFocus();
+ break;
+ }
+ parent = parent->GetParent();
+ }
+ }
+
m_editorFocused = 0;
}
else DoExpand( p, true );
}
- res = false;
+ // Do not Skip() the event after selection has been made.
+ // Otherwise default event handling behaviour kicks in
+ // and may revert focus back to the main canvas.
+ res = true;
}
else
{
m_draggedSplitter = splitterHit;
m_dragOffset = splitterHitOffset;
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+ #if wxPG_REFRESH_CONTROLS
// Fixes button disappearance bug
if ( m_wndEditor2 )
m_wndEditor2->Show ( false );
// -----------------------------------------------------------------------
-#if wxPG_SUPPORT_TOOLTIPS
-
-void wxPropertyGrid::SetToolTip( const wxString& tipString )
-{
- if ( tipString.length() )
- {
- wxScrolledWindow::SetToolTip(tipString);
- }
- else
- {
- #if wxPG_ALLOW_EMPTY_TOOLTIPS
- wxScrolledWindow::SetToolTip( m_emptyString );
- #else
- wxScrolledWindow::SetToolTip( NULL );
- #endif
- }
-}
-
-#endif // #if wxPG_SUPPORT_TOOLTIPS
-
-// -----------------------------------------------------------------------
-
// Return false if should be skipped
bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y,
wxMouseEvent &event )
{
int newSplitterX = x - m_dragOffset;
- int splitterX = x - splitterHitOffset;
// Splitter redraw required?
if ( newSplitterX != splitterX )
//
if ( m_windowStyle & wxPG_TOOLTIPS )
{
- wxToolTip* tooltip = GetToolTip();
-
if ( m_propHover != prevHover || prevSide != m_mouseSide )
{
if ( m_propHover && !m_propHover->IsCategory() )
int tw, th;
GetTextExtent( tipString, &tw, &th, 0, 0 );
if ( tw > space )
- {
SetToolTip( tipString );
- }
}
else
{
- if ( tooltip )
- {
- #if wxPG_ALLOW_EMPTY_TOOLTIPS
- SetToolTip( m_emptyString );
- #else
- wxScrolledWindow::SetToolTip( NULL );
- #endif
- }
+ SetToolTip( m_emptyString );
}
}
}
else
{
- if ( tooltip )
- {
- #if wxPG_ALLOW_EMPTY_TOOLTIPS
- SetToolTip( m_emptyString );
- #else
- wxScrolledWindow::SetToolTip( NULL );
- #endif
- }
+ SetToolTip( m_emptyString );
}
}
}
int splitterHitOffset;
state->HitTestH( x, &splitterHit, &splitterHitOffset );
- // No event type check - basicly calling this method should
+ // No event type check - basically calling this method should
// just stop dragging.
// Left up after dragged?
if ( m_dragStatus >= 1 )
// Disable splitter auto-centering (but only if moved any -
// otherwise we end up disabling auto-center even after a
// recentering double-click).
- int posDiff = abs(m_startingSplitterX -
+ int posDiff = abs(m_startingSplitterX -
GetSplitterPosition(m_draggedSplitter));
if ( posDiff > 1 )
m_wndEditor->Show ( true );
}
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+ #if wxPG_REFRESH_CONTROLS
// Fixes button disappearance bug
if ( m_wndEditor2 )
m_wndEditor2->Show ( true );
// Hide popup on clicks
if ( event.GetEventType() != wxEVT_MOTION )
- if ( wnd && wnd->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
+ if ( wxDynamicCast(wnd, wxOwnerDrawnComboBox) )
{
((wxOwnerDrawnComboBox*)wnd)->HidePopup();
}
int x, y;
if ( OnMouseCommon( event, &x, &y ) )
{
- HandleMouseClick(x,y,event);
+ if ( !HandleMouseClick(x, y, event) )
+ event.Skip();
+ }
+ else
+ {
+ event.Skip();
}
- event.Skip();
}
// -----------------------------------------------------------------------
int x, y;
CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
HandleMouseDoubleClick(x,y,event);
- event.Skip();
+
+ // Do not Skip() event here - OnMouseClick() call above
+ // should have already taken care of it.
}
// -----------------------------------------------------------------------
int x, y;
if ( OnMouseCommon( event, &x, &y ) )
{
- HandleMouseUp(x,y,event);
+ if ( !HandleMouseUp(x, y, event) )
+ event.Skip();
+ }
+ else
+ {
+ event.Skip();
}
- event.Skip();
}
// -----------------------------------------------------------------------
if ( keycode == WXK_TAB )
{
+#if defined(__WXGTK__)
wxWindow* mainControl;
if ( HasInternalFlag(wxPG_FL_IN_MANAGER) )
mainControl = GetParent();
else
mainControl = this;
+#endif
if ( !event.ShiftDown() )
{
return;
}
- // Except for TAB and ESC, handle child control events in child control
- if ( fromChild )
+ // Except for TAB, ESC, and any keys specifically dedicated to
+ // wxPropertyGrid itself, handle child control events in child control.
+ if ( fromChild &&
+ wxPGFindInVector(m_dedicatedKeys, keycode) == wxNOT_FOUND )
{
// Only propagate event if it had modifiers
if ( !event.HasModifiers() )
wxPGProperty* p = selected;
+ if ( action == wxPG_ACTION_EDIT && !editorFocused )
+ {
+ DoSelectProperty( p, wxPG_SEL_FOCUS );
+ wasHandled = true;
+ }
+
// Travel and expand/collapse
int selectDir = -2;
{
p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir );
if ( p )
- DoSelectProperty(p);
+ {
+ int selFlags = 0;
+ int reopenLabelEditorCol = -1;
+
+ if ( editorFocused )
+ {
+ // If editor was focused, then make the next editor
+ // focused as well
+ selFlags |= wxPG_SEL_FOCUS;
+ }
+ else
+ {
+ // Also maintain the same label editor focus state
+ if ( m_labelEditor )
+ reopenLabelEditorCol = m_selColumn;
+ }
+
+ DoSelectProperty(p, selFlags);
+
+ if ( reopenLabelEditorCol >= 0 )
+ DoBeginLabelEdit(reopenLabelEditorCol);
+ }
wasHandled = true;
}
}
if ( action == wxPG_ACTION_PRESS_BUTTON &&
m_wndEditor2 )
{
- wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, m_wndEditor2->GetId());
+ wxCommandEvent evt(wxEVT_BUTTON, m_wndEditor2->GetId());
GetEventHandler()->AddPendingEvent(evt);
return true;
}
// Called by focus event handlers. newFocused is the window that becomes focused.
void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
{
+ //
+ // Never allow focus to be changed when handling editor event.
+ // Especially because they may be displaing a dialog which
+ // could cause all kinds of weird (native) focus changes.
+ if ( HasInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT) )
+ return;
+
unsigned int oldFlags = m_iFlags;
bool wasEditorFocused = false;
wxWindow* wndEditor = m_wndEditor;
RegisterDefaultEditors();
wxString name = editorName;
- if ( name.length() == 0 )
+ if ( name.empty() )
name = editorClass->GetName();
// Existing editor under this name?
m_column = 1;
m_canVeto = false;
m_wasVetoed = false;
+ m_pg = NULL;
}
// -----------------------------------------------------------------------
return NULL;
}
- if ( !classInfo || !classInfo->IsKindOf(CLASSINFO(wxPGProperty)) )
+ if ( !classInfo || !classInfo->IsKindOf(wxCLASSINFO(wxPGProperty)) )
{
ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass.c_str()));
return NULL;
else
{
bool found = false;
- if ( idString.length() )
+ if ( !idString.empty() )
{
wxPGHashMapS2P::iterator it = m_dictIdChoices.find(idString);
if ( it != m_dictIdChoices.end() )
}
// Assign to id
- if ( idString.length() )
+ if ( !idString.empty() )
m_dictIdChoices[idString] = choices.GetData();
}
}
wxString valuel = value.Lower();
wxVariant variant;
- if ( type.length() == 0 )
+ if ( type.empty() )
{
long v;