// 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
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();
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_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();
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();
}
}
- 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 );
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();
{
if ( !m_doubleBuffer )
{
- paintFinishY = drawRect->y;
+ paintFinishY = itemsRect->y;
dcPtr = NULL;
}
else
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;
}
// 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;
//
// With wxPG_DOUBLE_BUFFER, do double buffering
- // - buffer's y = 0, so align drawRect and coordinates to that
+ // - buffer's y = 0, so align itemsRect and coordinates to that
//
#if wxPG_DOUBLE_BUFFER
int yRelMod = 0;
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;
}
//
// Return rect which encloses the given property range
// (in logical grid coordinates)
- //
+ //
int visTop = p1->GetY();
int visBottom;
(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 )
{
if ( m_inDoPropertyChanged )
return true;
+ 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);
SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL );
- m_inDoPropertyChanged = 0;
-
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
if ( m_labelEditor && event.GetId() == m_labelEditor->GetId() )
{
event.Skip();
- return;
+ return true;
}
wxPGProperty* selected = GetSelection();
// 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();
wxString newTcValue = tc->GetValue();
if ( m_prevTcValue == newTcValue )
- return;
+ return true;
m_prevTcValue = newTcValue;
}
else if ( wnd->IsKindOf(CLASSINFO(wxComboCtrl)) )
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 ( editor->OnEvent( this, selected, editorWnd, event ) )
{
+ result = true;
+
// If changes, validate them
if ( DoEditorValidate() )
{
// Let unhandled button click events go to the parent
if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
{
+ result = true;
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
GetEventHandler()->AddPendingEvent(evt);
}
}
ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
+
+ return result;
}
// -----------------------------------------------------------------------
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
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;
}
// 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;
}
}
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) )
{
+#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->length() )
{
- 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->length() &&
+ 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);
void wxPropertyGrid::SetFocusOnCanvas()
{
- SetFocusIgnoringChildren();
+ SetFocus();
m_editorFocused = 0;
}
else DoExpand( p, true );
}
- res = false;
+ // Do not Skip() the event after selection has been made.
+ // Otherwise default event handling behavior kicks in
+ // and may revert focus back to the main canvas.
+ res = true;
}
else
{
// -----------------------------------------------------------------------
-#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 )
//
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 );
}
}
}
// 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 )
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();
}
// -----------------------------------------------------------------------
wxPGProperty* p = selected;
+ if ( action == wxPG_ACTION_EDIT && !editorFocused )
+ {
+ DoSelectProperty( p, wxPG_SEL_FOCUS );
+ wasHandled = true;
+ }
+
// Travel and expand/collapse
int selectDir = -2;
// 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;