// 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)
if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
wxPropertyGrid::RegisterDefaultEditors();
+ m_validatingEditor = 0;
m_iFlags = 0;
m_pState = NULL;
m_wndEditor = m_wndEditor2 = NULL;
m_curFocused = NULL;
m_processedEvent = 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;
}
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;
+ wxDELETE(m_doubleBuffer);
#endif
}
}
// Control font changer helper.
void wxPropertyGrid::SetCurControlBoldFont()
{
- wxASSERT( m_wndEditor );
- m_wndEditor->SetFont( m_captionFont );
+ wxWindow* editor = GetEditorControl();
+ editor->SetFont( m_captionFont );
}
// -----------------------------------------------------------------------
//
// 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);
}
}
(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
pendingValue = value;
}
+ m_validationInfo.m_isFailing = false;
+
return true;
}
// -----------------------------------------------------------------------
+#if wxUSE_STATUSBAR
+wxStatusBar* wxPropertyGrid::GetStatusBar()
+{
+ wxWindow* topWnd = ::wxGetTopLevelParent(this);
+ if ( topWnd && topWnd->IsKindOf(CLASSINFO(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 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);
}
}
- 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.");
+ msg = _("You have entered invalid value. Press ESC to cancel editing.");
- DoShowPropertyError(property, msg);
+ #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);
+
+ 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) )
{
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;
}
if ( !m_pState )
return;
+ // 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;
+ }
+
wxPGProperty* selected = GetSelection();
// Somehow, event is handled after property has been deselected.
// 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_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
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
{
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: 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_COMMAND_TEXT_ENTER )
+ return true;
+
return wxEvtHandler::ProcessEvent(event);
}
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;
}
}
- 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 );
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();
- }
-
+ wxStatusBar* statusbar = GetStatusBar();
if ( statusbar )
{
const wxString* pHelpString = (const wxString*) NULL;
}
#endif
- m_inDoSelectProperty = 0;
-
// call wx event handler (here so that it also occurs on deselection)
if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) )
SendEvent( wxEVT_PG_SELECTED, p, NULL );
{
int newSplitterX = x - m_dragOffset;
- int splitterX = x - splitterHitOffset;
// Splitter redraw required?
if ( newSplitterX != splitterX )
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() )
{
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;
}
}
// 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;