// 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
//#define wxPG_TOOLTIP_DELAY 1000
+// This is the number of pixels the expander button inside
+// property cells (i.e. not in the grey margin area are
+// adjusted.
+#define IN_CELL_EXPANDER_BUTTON_X_ADJUST 2
+
// -----------------------------------------------------------------------
#if wxUSE_INTL
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)
m_strMin = wxS("Min");
m_strMax = wxS("Max");
m_strUnits = wxS("Units");
+ m_strHint = wxS("Hint");
+#if wxPG_COMPATIBILITY_1_4
m_strInlineHelp = wxS("InlineHelp");
+#endif
m_warnings = 0;
}
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;
+ // Set up default unspecified value 'colour'
+ m_unspecifiedAppearance.SetFgCol(*wxLIGHT_GREY);
+
// Set default keys
AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT );
AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
m_coloursCustomized = 0;
m_frozen = 0;
-#if wxPG_DOUBLE_BUFFER
m_doubleBuffer = NULL;
-#endif
#ifndef wxPG_ICON_WIDTH
m_expandbmp = NULL;
CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING );
- // Allocate cell datas indirectly by calling setter
- m_propertyDefaultCell.SetBgCol(*wxBLACK);
- m_categoryDefaultCell.SetBgCol(*wxBLACK);
+ // Allocate cell datas
+ m_propertyDefaultCell.SetEmptyData();
+ m_categoryDefaultCell.SetEmptyData();
RegainColours();
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.
wxArrayPGProperty selection = m_pState->m_selection;
- DoSetSelection(selection, wxPG_SEL_FORCE);
+ DoSetSelection(selection, wxPG_SEL_FORCE | wxPG_SEL_NONVISIBLE);
}
}
wxMouseEvent* mouseEvent,
int selFlags )
{
+ const wxArrayPGProperty& selection = GetSelectedProperties();
bool alreadySelected = m_pState->DoIsPropertySelected(prop);
bool res = true;
- bool addToExistingSelection;
+
+ // Set to 2 if also add all items in between
+ int addToExistingSelection = 0;
if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION )
{
}
else
{
- addToExistingSelection = mouseEvent->ShiftDown();
+ if ( mouseEvent->ControlDown() )
+ {
+ addToExistingSelection = 1;
+ }
+ else if ( mouseEvent->ShiftDown() )
+ {
+ if ( selection.size() > 0 && !prop->IsCategory() )
+ addToExistingSelection = 2;
+ else
+ addToExistingSelection = 1;
+ }
}
}
- else
- {
- addToExistingSelection = false;
- }
- }
- else
- {
- addToExistingSelection = false;
}
- if ( addToExistingSelection )
+ if ( addToExistingSelection == 1 )
{
+ // Add/remove one
if ( !alreadySelected )
{
res = DoAddToSelection(prop, selFlags);
res = DoRemoveFromSelection(prop, selFlags);
}
}
+ else if ( addToExistingSelection == 2 )
+ {
+ // Add this, and all in between
+
+ // Find top selected property
+ wxPGProperty* topSelProp = selection[0];
+ int topSelPropY = topSelProp->GetY();
+ for ( unsigned int i=1; i<selection.size(); i++ )
+ {
+ wxPGProperty* p = selection[i];
+ int y = p->GetY();
+ if ( y < topSelPropY )
+ {
+ topSelProp = p;
+ topSelPropY = y;
+ }
+ }
+
+ wxPGProperty* startFrom;
+ wxPGProperty* stopAt;
+
+ if ( prop->GetY() <= topSelPropY )
+ {
+ // Property is above selection (or same)
+ startFrom = prop;
+ stopAt = topSelProp;
+ }
+ else
+ {
+ // Property is below selection
+ startFrom = topSelProp;
+ stopAt = prop;
+ }
+
+ // Iterate through properties in-between, and select them
+ wxPropertyGridIterator it;
+
+ for ( it = GetIterator(wxPG_ITERATE_VISIBLE, startFrom);
+ !it.AtEnd();
+ it++ )
+ {
+ wxPGProperty* p = *it;
+
+ if ( !p->IsCategory() &&
+ !m_pState->DoIsPropertySelected(p) )
+ {
+ DoAddToSelection(p, selFlags);
+ }
+
+ if ( p == stopAt )
+ break;
+ }
+ }
else
{
res = DoSelectAndEdit(prop, colIndex, selFlags);
}
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);
wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
m_colPropBack = bgCol;
m_propertyDefaultCell.GetData()->SetBgCol(bgCol);
+ if ( !m_unspecifiedAppearance.GetBgCol().IsOk() )
+ m_unspecifiedAppearance.SetBgCol(bgCol);
}
if ( !(m_coloursCustomized & 0x0010) )
wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
m_colPropFore = fgCol;
m_propertyDefaultCell.GetData()->SetFgCol(fgCol);
+ if ( !m_unspecifiedAppearance.GetFgCol().IsOk() )
+ m_unspecifiedAppearance.SetFgCol(fgCol);
}
if ( !(m_coloursCustomized & 0x0020) )
// 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 );
m_coloursCustomized |= 0x08;
m_propertyDefaultCell.GetData()->SetBgCol(col);
+ m_unspecifiedAppearance.SetBgCol(col);
Refresh();
}
m_coloursCustomized |= 0x10;
m_propertyDefaultCell.GetData()->SetFgCol(col);
+ m_unspecifiedAppearance.SetFgCol(col);
Refresh();
}
Sort(wxPG_SORT_TOP_LEVEL_ONLY);
RecalculateVirtualSize();
+
+ // Fix editor position
+ CorrectEditorWidgetPosY();
}
// -----------------------------------------------------------------------
// 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;
// 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;
+ 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 wxPGProperty* firstSelected = GetSelection();
const wxPropertyGridPageState* state = m_pState;
-
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- bool wasSelectedPainted = false;
-#endif
+ const wxArrayInt& colWidths = state->m_colWidths;
// TODO: Only render columns that are within clipping region.
unsigned int si;
int sx = x;
- for ( si=0; si<state->m_colWidths.size(); si++ )
+ for ( si=0; si<colWidths.size(); si++ )
{
- sx += state->m_colWidths[si];
+ sx += colWidths[si];
dc.DrawLine( sx, y, sx, y2 );
}
}
else
{
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- if ( p == firstSelected )
- wasSelectedPainted = true;
-#endif
-
renderFlags |= wxPGCellRenderer::Selected;
if ( !p->IsCategory() )
m_marginWidth,
lh );
- if ( p->IsCategory() )
+ // Default cell rect fill the entire row
+ wxRect cellRect(greyDepthX, y,
+ gridWidth - greyDepth + 2, rowHeight-1 );
+
+ bool isCategory = p->IsCategory();
+
+ if ( isCategory )
{
- // Captions have their cell areas merged as one
dc.SetFont(m_captionFont);
fontChanged = true;
- wxRect cellRect(greyDepthX, y, gridWidth - greyDepth + 2, rowHeight-1 );
if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
{
{
dc.SetTextForeground(rowFgCol);
}
-
- wxPGCellRenderer* renderer = p->GetCellRenderer(0);
- renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags );
-
- // Tree Item Button
- if ( !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
- DrawExpanderButton( dc, butRect, p );
}
else
{
- if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) )
+ // Fine tune button rectangle to actually fit the cell
+ if ( butRect.x > 0 )
+ butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST;
+
+ if ( p->m_flags & wxPG_PROP_MODIFIED &&
+ (windowStyle & wxPG_BOLD_MODIFIED) )
{
dc.SetFont(m_captionFont);
fontChanged = true;
}
- unsigned int ci;
- int cellX = x + 1;
- int nextCellWidth = state->m_colWidths[0] -
- (greyDepthX - m_marginWidth);
- wxRect cellRect(greyDepthX+1, y, 0, rowHeight-1);
- int textXAdd = textMarginHere - greyDepthX;
+ // Magic fine-tuning for non-category rows
+ cellRect.x += 1;
+ }
+
+ int firstCellWidth = colWidths[0] - (greyDepthX - m_marginWidth);
+ int firstCellX = cellRect.x;
+
+ // Calculate cellRect.x for the last cell
+ unsigned int ci = 0;
+ int cellX = x + 1;
+ for ( ci=0; ci<colWidths.size(); ci++ )
+ cellX += colWidths[ci];
+ cellRect.x = cellX;
+
+ // Draw cells from back to front so that we can easily tell if the
+ // cell on the right was empty from text
+ bool prevFilled = true;
+ ci = colWidths.size();
+ do
+ {
+ ci--;
+
+ int textXAdd = 0;
+
+ if ( ci == 0 )
+ {
+ textXAdd = textMarginHere - greyDepthX;
+ cellRect.width = firstCellWidth;
+ cellRect.x = firstCellX;
+ }
+ else
+ {
+ int colWidth = colWidths[ci];
+ cellRect.width = colWidth;
+ cellRect.x -= colWidth;
+ }
- for ( ci=0; ci<state->m_colWidths.size(); ci++ )
+ // Merge with column to the right?
+ if ( !prevFilled && isCategory )
{
- cellRect.width = nextCellWidth - 1;
+ cellRect.width += colWidths[ci+1];
+ }
+
+ if ( !isCategory )
+ cellRect.width -= 1;
- wxWindow* cellEditor = NULL;
- int cellRenderFlags = renderFlags;
+ wxWindow* cellEditor = NULL;
+ int cellRenderFlags = renderFlags;
- // Tree Item Button (must be drawn before clipping is set up)
- if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
- DrawExpanderButton( dc, butRect, p );
+ // Tree Item Button (must be drawn before clipping is set up)
+ if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
+ DrawExpanderButton( dc, butRect, p );
- // Background
- if ( isSelected && (ci == 1 || ci == m_selColumn) )
+ // Background
+ if ( isSelected && (ci == 1 || ci == m_selColumn) )
+ {
+ if ( p == firstSelected )
{
- if ( p == firstSelected )
- {
- if ( ci == 1 && m_wndEditor )
- cellEditor = m_wndEditor;
- else if ( ci == m_selColumn && m_labelEditor )
- cellEditor = m_labelEditor;
- }
+ if ( ci == 1 && m_wndEditor )
+ cellEditor = m_wndEditor;
+ else if ( ci == m_selColumn && m_labelEditor )
+ cellEditor = m_labelEditor;
+ }
- if ( cellEditor )
- {
- wxColour editorBgCol =
- cellEditor->GetBackgroundColour();
- dc.SetBrush(editorBgCol);
- dc.SetPen(editorBgCol);
+ if ( cellEditor )
+ {
+ wxColour editorBgCol =
+ cellEditor->GetBackgroundColour();
+ dc.SetBrush(editorBgCol);
+ dc.SetPen(editorBgCol);
+ dc.SetTextForeground(m_colPropFore);
+ dc.DrawRectangle(cellRect);
+
+ if ( m_dragStatus != 0 ||
+ (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) )
+ cellEditor = NULL;
+ }
+ else
+ {
+ dc.SetBrush(m_colPropBack);
+ dc.SetPen(m_colPropBack);
+ if ( p->IsEnabled() )
dc.SetTextForeground(m_colPropFore);
- dc.DrawRectangle(cellRect);
-
- if ( m_dragStatus != 0 ||
- (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) )
- cellEditor = NULL;
- }
else
- {
- dc.SetBrush(m_colPropBack);
- dc.SetPen(m_colPropBack);
dc.SetTextForeground(m_colDisPropFore);
- if ( p->IsEnabled() )
- dc.SetTextForeground(rowFgCol);
- else
- dc.SetTextForeground(m_colDisPropFore);
- }
}
- else
+ }
+ else
+ {
+ if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
{
- if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
- {
- dc.SetBrush(rowBgBrush);
- dc.SetPen(rowBgCol);
- }
+ dc.SetBrush(rowBgBrush);
+ dc.SetPen(rowBgCol);
+ }
- if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol )
- {
- dc.SetTextForeground(rowFgCol);
- }
+ if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol )
+ {
+ dc.SetTextForeground(rowFgCol);
}
+ }
- dc.SetClippingRegion(cellRect);
+ dc.SetClippingRegion(cellRect);
- cellRect.x += textXAdd;
- cellRect.width -= textXAdd;
+ cellRect.x += textXAdd;
+ cellRect.width -= textXAdd;
- // Foreground
- if ( !cellEditor )
+ // Foreground
+ if ( !cellEditor )
+ {
+ wxPGCellRenderer* renderer;
+ int cmnVal = p->GetCommonValue();
+ if ( cmnVal == -1 || ci != 1 )
{
- wxPGCellRenderer* renderer;
- int cmnVal = p->GetCommonValue();
- if ( cmnVal == -1 || ci != 1 )
- {
- renderer = p->GetCellRenderer(ci);
- renderer->Render( dc, cellRect, this, p, ci, -1,
- cellRenderFlags );
- }
- else
- {
- renderer = GetCommonValue(cmnVal)->GetRenderer();
- renderer->Render( dc, cellRect, this, p, ci, -1,
- cellRenderFlags );
- }
+ renderer = p->GetCellRenderer(ci);
+ prevFilled = renderer->Render(dc, cellRect, this,
+ p, ci, -1,
+ cellRenderFlags );
+ }
+ else
+ {
+ renderer = GetCommonValue(cmnVal)->GetRenderer();
+ prevFilled = renderer->Render(dc, cellRect, this,
+ p, ci, -1,
+ cellRenderFlags );
}
-
- cellX += state->m_colWidths[ci];
- if ( ci < (state->m_colWidths.size()-1) )
- nextCellWidth = state->m_colWidths[ci+1];
- cellRect.x = cellX;
- dc.DestroyClippingRegion(); // Is this really necessary?
- textXAdd = 0;
}
+ else
+ {
+ prevFilled = true;
+ }
+
+ dc.DestroyClippingRegion(); // Is this really necessary?
}
+ while ( ci > 0 );
if ( fontChanged )
dc.SetFont(normalFont);
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();
// 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,
+ int splitterIndex,
+ int flags )
{
if ( ( newxpos < wxPG_DRAG_MARGIN ) )
return;
wxPropertyGridPageState* state = m_pState;
- state->DoSetSplitterPosition( newxpos, splitterIndex, allPages );
+ if ( flags & wxPG_SPLITTER_FROM_EVENT )
+ state->m_dontCenterSplitter = true;
+
+ state->DoSetSplitterPosition(newxpos, splitterIndex, flags);
- if ( refresh )
+ if ( flags & wxPG_SPLITTER_REFRESH )
{
if ( GetSelection() )
CorrectEditorWidgetSizeX();
Refresh();
}
+
+ return;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing )
+{
+ wxPropertyGridPageState* state = m_pState;
+ if ( state )
+ state->ResetColumnSizes(0);
+
+ if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
+ m_pState->m_dontCenterSplitter = false;
}
// -----------------------------------------------------------------------
-void wxPropertyGrid::CenterSplitter( bool enableAutoCentering )
+void wxPropertyGrid::CenterSplitter( bool enableAutoResizing )
{
SetSplitterPosition( m_width/2 );
- if ( enableAutoCentering && HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
+ if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
m_pState->m_dontCenterSplitter = false;
}
(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.");
- 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) )
{
}
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 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();
// 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_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 &&
- wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
+ if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd )
{
- wxTextCtrl* tc = (wxTextCtrl*) wnd;
+ if ( wxDynamicCast(wnd, wxTextCtrl) )
+ {
+ wxTextCtrl* tc = (wxTextCtrl*) wnd;
- wxString newTcValue = tc->GetValue();
- if ( m_prevTcValue == newTcValue )
- return;
+ wxString newTcValue = tc->GetValue();
+ if ( m_prevTcValue == newTcValue )
+ return true;
+ m_prevTcValue = newTcValue;
+ }
+ 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;
- m_prevTcValue = newTcValue;
+ wxComboCtrl* cc = (wxComboCtrl*) wnd;
+
+ wxString newTcValue = cc->GetTextCtrl()->GetValue();
+ if ( m_prevTcValue == newTcValue )
+ return true;
+ m_prevTcValue = newTcValue;
+ }
}
SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
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() )
{
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
{
// 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;
}
// -----------------------------------------------------------------------
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_curcursor = type;
}
+// -----------------------------------------------------------------------
+
+wxString
+wxPropertyGrid::GetUnspecifiedValueText( int argFlags ) const
+{
+ const wxPGCell& ua = GetUnspecifiedValueAppearance();
+
+ if ( ua.HasText() &&
+ !(argFlags & wxPG_FULL_VALUE) &&
+ !(argFlags & wxPG_EDITABLE_VALUE) )
+ return ua.GetText();
+
+ return wxEmptyString;
+}
+
// -----------------------------------------------------------------------
// wxPropertyGrid property selection, editor creation
// -----------------------------------------------------------------------
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_COMMAND_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 );
wxRect grect = GetEditorWidgetRect(p, m_selColumn);
wxPoint goodPos = grect.GetPosition();
+ // Editor appearance can now be considered clear
+ m_editorAppearance.SetEmptyData();
+
const wxPGEditor* editor = p->GetEditorClass();
wxCHECK_MSG(editor, false,
wxT("NULL editor class not allowed"));
m_wndEditor = wndList.m_primary;
m_wndEditor2 = wndList.m_secondary;
- wxWindow* primaryCtrl = GetEditorControl();
+ primaryCtrl = GetEditorControl();
//
// Essentially, primaryCtrl == m_wndEditor
p->GetEditorClass()->OnFocus(p, primaryCtrl);
}
+ else
+ {
+ if ( p->IsValueUnspecified() )
+ SetEditorAppearance(m_unspecifiedAppearance,
+ true);
+ }
}
if ( m_wndEditor2 )
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) )
editorClass->UpdateControl(p, wnd);
if ( p->IsValueUnspecified() )
- editorClass ->SetValueToUnspecified(p, wnd);
+ SetEditorAppearance(m_unspecifiedAppearance, true);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::RecalculateVirtualSize( int forceXPos )
{
- if ( (m_iFlags & wxPG_FL_RECALCULATING_VIRTUAL_SIZE) || m_frozen )
+ // Don't check for !HasInternalFlag(wxPG_FL_INITIALIZED) here. Otherwise
+ // virtual size calculation may go wrong.
+ if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE) ||
+ m_frozen ||
+ !m_pState )
return;
//
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;
}
unsigned int selFlags,
unsigned int column )
{
+ // selFlags should have wxPG_SEL_NOVALIDATE if event is not
+ // vetoable.
+
// Send property grid event of specific type and with specific property
wxPropertyGridEvent evt( eventType, m_eventObject->GetId() );
evt.SetPropertyGrid(this);
evt.SetCanVeto(true);
}
+ wxPropertyGridEvent* prevProcessedEvent = m_processedEvent;
m_processedEvent = &evt;
m_eventObject->HandleWindowEvent(evt);
- m_processedEvent = NULL;
+ m_processedEvent = prevProcessedEvent;
return evt.WasVetoed();
}
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
{
if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
{
// Double-clicking the splitter causes auto-centering
- CenterSplitter( true );
+ if ( m_pState->GetColumnCount() <= 2 )
+ {
+ ResetColumnSizes( true );
+
+ SendEvent(wxEVT_PG_COL_DRAGGING,
+ m_propHover,
+ NULL,
+ wxPG_SEL_NOVALIDATE,
+ (unsigned int)m_draggedSplitter);
+ }
}
else if ( m_dragStatus == 0 )
{
// send event
DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
- if ( m_wndEditor )
+ // Allow application to veto dragging
+ if ( !SendEvent(wxEVT_PG_COL_BEGIN_DRAG,
+ p, NULL, 0,
+ (unsigned int)splitterHit) )
{
- // Changes must be committed here or the
- // value won't be drawn correctly
- if ( !CommitChangesFromEditor() )
- return res;
+ if ( m_wndEditor )
+ {
+ // Changes must be committed here or the
+ // value won't be drawn correctly
+ if ( !CommitChangesFromEditor() )
+ return res;
- m_wndEditor->Show ( false );
- }
+ m_wndEditor->Show ( false );
+ }
- if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
- {
- CaptureMouse();
- m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
- }
+ if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
+ {
+ CaptureMouse();
+ m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
+ }
- m_dragStatus = 1;
- m_draggedSplitter = splitterHit;
- m_dragOffset = splitterHitOffset;
+ m_dragStatus = 1;
+ m_draggedSplitter = splitterHit;
+ m_dragOffset = splitterHitOffset;
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- // Fixes button disappearance bug
- if ( m_wndEditor2 )
- m_wndEditor2->Show ( false );
- #endif
+ #if wxPG_REFRESH_CONTROLS
+ // Fixes button disappearance bug
+ if ( m_wndEditor2 )
+ m_wndEditor2->Show ( false );
+ #endif
- m_startingSplitterX = x - splitterHitOffset;
+ m_startingSplitterX = x - splitterHitOffset;
+ }
}
}
}
{
int nx = x + m_marginWidth - marginEnds; // Normalize x.
+ // Fine tune cell button x
+ if ( !p->IsCategory() )
+ nx -= IN_CELL_EXPANDER_BUTTON_X_ADJUST;
+
if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
{
int y2 = y % m_lineHeight;
// -----------------------------------------------------------------------
-#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 )
{
// Move everything
- state->m_dontCenterSplitter = true;
- state->DoSetSplitterPosition(newSplitterX,
- m_draggedSplitter,
- false);
- state->m_fSplitterX = (float) newSplitterX;
-
- if ( GetSelection() )
- CorrectEditorWidgetSizeX();
-
- Update();
- Refresh();
+ DoSetSplitterPosition(newSplitterX,
+ m_draggedSplitter,
+ wxPG_SPLITTER_REFRESH |
+ wxPG_SPLITTER_FROM_EVENT);
+
+ SendEvent(wxEVT_PG_COL_DRAGGING,
+ m_propHover,
+ NULL,
+ wxPG_SEL_NOVALIDATE,
+ (unsigned int)m_draggedSplitter);
}
m_dragStatus = 2;
//
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 )
// (it is only here as a reminder to not to do it)
//splitterX = x;
- // Disable splitter auto-centering
- state->m_dontCenterSplitter = true;
+ SendEvent(wxEVT_PG_COL_END_DRAG,
+ m_propHover,
+ NULL,
+ wxPG_SEL_NOVALIDATE,
+ (unsigned int)m_draggedSplitter);
+
+ // 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 -
+ GetSplitterPosition(m_draggedSplitter));
+
+ if ( posDiff > 1 )
+ state->m_dontCenterSplitter = true;
// This is necessary to return cursor
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
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 ( tlp != m_tlp )
OnTLPChanging(tlp);
}
+
+ //
+ // Resolve pending property removals
+ if ( m_deletedProperties.size() > 0 )
+ {
+ wxArrayPGProperty& arr = m_deletedProperties;
+ for ( unsigned int i=0; i<arr.size(); i++ )
+ {
+ DeleteProperty(arr[i]);
+ }
+ arr.clear();
+ }
+ if ( m_removedProperties.size() > 0 )
+ {
+ wxArrayPGProperty& arr = m_removedProperties;
+ for ( unsigned int i=0; i<arr.size(); i++ )
+ {
+ RemoveProperty(arr[i]);
+ }
+ arr.clear();
+ }
}
bool wxPropertyGrid::IsEditorFocused() const
// 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;
m_iFlags &= ~(wxPG_FL_FOCUSED);
// This must be one of nextFocus' parents.
while ( parent )
{
+ if ( parent == wndEditor )
+ {
+ wasEditorFocused = true;
+ }
// Use m_eventObject, which is either wxPropertyGrid or
// wxPropertyGridManager, as appropriate.
- if ( parent == m_eventObject )
+ else if ( parent == m_eventObject )
{
m_iFlags |= wxPG_FL_FOCUSED;
break;
parent = parent->GetParent();
}
+ // Notify editor control when it receives a focus
+ if ( wasEditorFocused && m_curFocused != newFocused )
+ {
+ wxPGProperty* p = GetSelection();
+ if ( p )
+ {
+ const wxPGEditor* editor = p->GetEditorClass();
+ ResetEditorAppearance();
+ editor->OnFocus(p, GetEditorControl());
+ }
+ }
+
m_curFocused = newFocused;
if ( (m_iFlags & wxPG_FL_FOCUSED) !=
RegisterDefaultEditors();
wxString name = editorName;
- if ( name.length() == 0 )
+ if ( name.empty() )
name = editorClass->GetName();
// Existing editor under this name?
wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent );
wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN, wxPropertyGridEvent );
wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_BEGIN_DRAG, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_DRAGGING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_END_DRAG, wxPropertyGridEvent );
// -----------------------------------------------------------------------
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;