1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/propgrid/propgrid.cpp 
   3 // Purpose:     wxPropertyGrid 
   4 // Author:      Jaakko Salli 
   8 // Copyright:   (c) Jaakko Salli 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx/wx.h". 
  13 #include "wx/wxprec.h" 
  23     #include "wx/object.h" 
  25     #include "wx/string.h" 
  28     #include "wx/window.h" 
  31     #include "wx/dcmemory.h" 
  32     #include "wx/button.h" 
  35     #include "wx/cursor.h" 
  36     #include "wx/dialog.h" 
  37     #include "wx/settings.h" 
  38     #include "wx/msgdlg.h" 
  39     #include "wx/choice.h" 
  40     #include "wx/stattext.h" 
  41     #include "wx/scrolwin.h" 
  42     #include "wx/dirdlg.h" 
  44     #include "wx/textdlg.h" 
  45     #include "wx/filedlg.h" 
  46     #include "wx/statusbr.h" 
  52 // This define is necessary to prevent macro clearing 
  53 #define __wxPG_SOURCE_FILE__ 
  55 #include "wx/propgrid/propgrid.h" 
  56 #include "wx/propgrid/editors.h" 
  58 #if wxPG_USE_RENDERER_NATIVE 
  59     #include "wx/renderer.h" 
  62 #include "wx/odcombo.h" 
  65 #include "wx/dcbuffer.h" 
  66 #include "wx/scopeguard.h" 
  68 // Two pics for the expand / collapse buttons. 
  69 // Files are not supplied with this project (since it is 
  70 // recommended to use either custom or native rendering). 
  71 // If you want them, get wxTreeMultiCtrl by Jorgen Bodde, 
  72 // and copy xpm files from archive to wxPropertyGrid src directory 
  73 // (and also comment/undef wxPG_ICON_WIDTH in propGrid.h 
  74 // and set wxPG_USE_RENDERER_NATIVE to 0). 
  75 #ifndef wxPG_ICON_WIDTH 
  76   #if defined(__WXMAC__) 
  77     #include "mac_collapse.xpm" 
  78     #include "mac_expand.xpm" 
  79   #elif defined(__WXGTK__) 
  80     #include "linux_collapse.xpm" 
  81     #include "linux_expand.xpm" 
  83     #include "default_collapse.xpm" 
  84     #include "default_expand.xpm" 
  89 //#define wxPG_TEXT_INDENT                4 // For the wxComboControl 
  90 //#define wxPG_ALLOW_CLIPPING             1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored 
  91 #define wxPG_GUTTER_DIV                 3 // gutter is max(iconwidth/gutter_div,gutter_min) 
  92 #define wxPG_GUTTER_MIN                 3 // gutter before and after image of [+] or [-] 
  93 #define wxPG_YSPACING_MIN               1 
  94 #define wxPG_DEFAULT_VSPACING           2 // This matches .NET propertygrid's value, 
  95                                           // but causes normal combobox to spill out under MSW 
  97 //#define wxPG_OPTIMAL_WIDTH              200 // Arbitrary 
  99 //#define wxPG_MIN_SCROLLBAR_WIDTH        10 // Smallest scrollbar width on any platform 
 100                                            // Must be larger than largest control border 
 104 #define wxPG_DEFAULT_CURSOR             wxNullCursor 
 107 //#define wxPG_NAT_CHOICE_BORDER_ANY   0 
 109 //#define wxPG_HIDER_BUTTON_HEIGHT        25 
 111 #define wxPG_PIXELS_PER_UNIT            m_lineHeight 
 113 #ifdef wxPG_ICON_WIDTH 
 114   #define m_iconHeight m_iconWidth 
 117 //#define wxPG_TOOLTIP_DELAY              1000 
 119 // This is the number of pixels the expander button inside 
 120 // property cells (i.e. not in the grey margin area are 
 122 #define IN_CELL_EXPANDER_BUTTON_X_ADJUST    2 
 124 // ----------------------------------------------------------------------- 
 127 void wxPropertyGrid::AutoGetTranslation ( bool enable 
) 
 129     wxPGGlobalVars
->m_autoGetTranslation 
= enable
; 
 132 void wxPropertyGrid::AutoGetTranslation ( bool ) { } 
 135 // ----------------------------------------------------------------------- 
 137 const char wxPropertyGridNameStr
[] = "wxPropertyGrid"; 
 139 // ----------------------------------------------------------------------- 
 140 // Statics in one class for easy destruction. 
 141 // ----------------------------------------------------------------------- 
 143 #include "wx/module.h" 
 145 class wxPGGlobalVarsClassManager 
: public wxModule
 
 147     DECLARE_DYNAMIC_CLASS(wxPGGlobalVarsClassManager
) 
 149     wxPGGlobalVarsClassManager() {} 
 150     virtual bool OnInit() { wxPGGlobalVars 
= new wxPGGlobalVarsClass(); return true; } 
 151     virtual void OnExit() { wxDELETE(wxPGGlobalVars
); } 
 154 IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager
, wxModule
) 
 157 // When wxPG is loaded dynamically after the application is already running 
 158 // then the built-in module system won't pick this one up.  Add it manually. 
 159 void wxPGInitResourceModule() 
 161     wxModule
* module = new wxPGGlobalVarsClassManager
; 
 163     wxModule::RegisterModule(module); 
 166 wxPGGlobalVarsClass
* wxPGGlobalVars 
= NULL
; 
 169 wxPGGlobalVarsClass::wxPGGlobalVarsClass() 
 171     wxPGProperty::sm_wxPG_LABEL 
= new wxString(wxPG_LABEL_STRING
); 
 173     m_boolChoices
.Add(_("False")); 
 174     m_boolChoices
.Add(_("True")); 
 176     m_fontFamilyChoices 
= NULL
; 
 178     m_defaultRenderer 
= new wxPGDefaultRenderer(); 
 180     m_autoGetTranslation 
= false; 
 188     // Prepare some shared variants 
 189     m_vEmptyString 
= wxString(); 
 191     m_vMinusOne 
= (long) -1; 
 195     // Prepare cached string constants 
 196     m_strstring 
= wxS("string"); 
 197     m_strlong 
= wxS("long"); 
 198     m_strbool 
= wxS("bool"); 
 199     m_strlist 
= wxS("list"); 
 200     m_strDefaultValue 
= wxS("DefaultValue"); 
 201     m_strMin 
= wxS("Min"); 
 202     m_strMax 
= wxS("Max"); 
 203     m_strUnits 
= wxS("Units"); 
 204     m_strHint 
= wxS("Hint"); 
 205 #if wxPG_COMPATIBILITY_1_4 
 206     m_strInlineHelp 
= wxS("InlineHelp"); 
 213 wxPGGlobalVarsClass::~wxPGGlobalVarsClass() 
 217     delete m_defaultRenderer
; 
 219     // This will always have one ref 
 220     delete m_fontFamilyChoices
; 
 223     for ( i
=0; i
<m_arrValidators
.size(); i
++ ) 
 224         delete ((wxValidator
*)m_arrValidators
[i
]); 
 228     // Destroy value type class instances. 
 229     wxPGHashMapS2P::iterator vt_it
; 
 231     // Destroy editor class instances. 
 232     // iterate over all the elements in the class 
 233     for( vt_it 
= m_mapEditorClasses
.begin(); vt_it 
!= m_mapEditorClasses
.end(); ++vt_it 
) 
 235         delete ((wxPGEditor
*)vt_it
->second
); 
 238     delete wxPGProperty::sm_wxPG_LABEL
; 
 241 void wxPropertyGridInitGlobalsIfNeeded() 
 245 // ----------------------------------------------------------------------- 
 247 // ----------------------------------------------------------------------- 
 249 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid
, wxControl
) 
 251 BEGIN_EVENT_TABLE(wxPropertyGrid
, wxControl
) 
 252   EVT_IDLE(wxPropertyGrid::OnIdle
) 
 253   EVT_PAINT(wxPropertyGrid::OnPaint
) 
 254   EVT_SIZE(wxPropertyGrid::OnResize
) 
 255   EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry
) 
 256   EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry
) 
 257   EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange
) 
 258   EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent
) 
 259   EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent
) 
 260   EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent
) 
 261   EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent
) 
 262   EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged
) 
 263   EVT_MOTION(wxPropertyGrid::OnMouseMove
) 
 264   EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick
) 
 265   EVT_LEFT_UP(wxPropertyGrid::OnMouseUp
) 
 266   EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick
) 
 267   EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick
) 
 268   EVT_KEY_DOWN(wxPropertyGrid::OnKey
) 
 271 // ----------------------------------------------------------------------- 
 273 wxPropertyGrid::wxPropertyGrid() 
 274     : wxControl(), wxScrollHelper(this) 
 279 // ----------------------------------------------------------------------- 
 281 wxPropertyGrid::wxPropertyGrid( wxWindow 
*parent
, 
 286                                 const wxString
& name 
) 
 287     : wxControl(), wxScrollHelper(this) 
 290     Create(parent
,id
,pos
,size
,style
,name
); 
 293 // ----------------------------------------------------------------------- 
 295 bool wxPropertyGrid::Create( wxWindow 
*parent
, 
 300                              const wxString
& name 
) 
 303     if (!(style
&wxBORDER_MASK
)) 
 305         style 
|= wxBORDER_THEME
; 
 310     // Filter out wxTAB_TRAVERSAL - we will handle TABs manually 
 311     style 
&= ~(wxTAB_TRAVERSAL
); 
 312     style 
|= wxWANTS_CHARS
; 
 314     wxControl::Create(parent
, id
, pos
, size
, 
 315                       style 
| wxScrolledWindowStyle
, 
 324 // ----------------------------------------------------------------------- 
 327 // Initialize values to defaults 
 329 void wxPropertyGrid::Init1() 
 331     // Register editor classes, if necessary. 
 332     if ( wxPGGlobalVars
->m_mapEditorClasses
.empty() ) 
 333         wxPropertyGrid::RegisterDefaultEditors(); 
 335     m_validatingEditor 
= 0; 
 338     m_wndEditor 
= m_wndEditor2 
= NULL
; 
 342     m_labelEditor 
= NULL
; 
 343     m_labelEditorProperty 
= NULL
; 
 344     m_eventObject 
= this; 
 346     m_processedEvent 
= NULL
; 
 347     m_sortFunction 
= NULL
; 
 348     m_inDoPropertyChanged 
= false; 
 349     m_inCommitChangesFromEditor 
= false; 
 350     m_inDoSelectProperty 
= false; 
 351     m_inOnValidationFailure 
= false; 
 352     m_permanentValidationFailureBehavior 
= wxPG_VFB_DEFAULT
; 
 357     // Set up default unspecified value 'colour' 
 358     m_unspecifiedAppearance
.SetFgCol(*wxLIGHT_GREY
); 
 361     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY
, WXK_RIGHT 
); 
 362     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY
, WXK_DOWN 
); 
 363     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY
, WXK_LEFT 
); 
 364     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY
, WXK_UP 
); 
 365     AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY
, WXK_RIGHT
); 
 366     AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY
, WXK_LEFT
); 
 367     AddActionTrigger( wxPG_ACTION_CANCEL_EDIT
, WXK_ESCAPE 
); 
 368     AddActionTrigger( wxPG_ACTION_PRESS_BUTTON
, WXK_DOWN
, wxMOD_ALT 
); 
 369     AddActionTrigger( wxPG_ACTION_PRESS_BUTTON
, WXK_F4 
); 
 371     m_coloursCustomized 
= 0; 
 374 #if wxPG_DOUBLE_BUFFER 
 375     m_doubleBuffer 
= NULL
; 
 378 #ifndef wxPG_ICON_WIDTH 
 384     m_iconWidth 
= wxPG_ICON_WIDTH
; 
 389     m_gutterWidth 
= wxPG_GUTTER_MIN
; 
 390     m_subgroup_extramargin 
= 10; 
 394     m_width 
= m_height 
= 0; 
 396     m_commonValues
.push_back(new wxPGCommonValue(_("Unspecified"), wxPGGlobalVars
->m_defaultRenderer
) ); 
 399     m_chgInfo_changedProperty 
= NULL
; 
 402 // ----------------------------------------------------------------------- 
 405 // Initialize after parent etc. set 
 407 void wxPropertyGrid::Init2() 
 409     wxASSERT( !(m_iFlags 
& wxPG_FL_INITIALIZED 
) ); 
 412    // Smaller controls on Mac 
 413    SetWindowVariant(wxWINDOW_VARIANT_SMALL
); 
 416     // Now create state, if one didn't exist already 
 417     // (wxPropertyGridManager might have created it for us). 
 420         m_pState 
= CreateState(); 
 421         m_pState
->m_pPropGrid 
= this; 
 422         m_iFlags 
|= wxPG_FL_CREATEDSTATE
; 
 425     if ( !(m_windowStyle 
& wxPG_SPLITTER_AUTO_CENTER
) ) 
 426         m_pState
->m_dontCenterSplitter 
= true; 
 428     if ( m_windowStyle 
& wxPG_HIDE_CATEGORIES 
) 
 430         m_pState
->InitNonCatMode(); 
 432         m_pState
->m_properties 
= m_pState
->m_abcArray
; 
 435     GetClientSize(&m_width
,&m_height
); 
 437 #ifndef wxPG_ICON_WIDTH 
 438     // create two bitmap nodes for drawing 
 439     m_expandbmp 
= new wxBitmap(expand_xpm
); 
 440     m_collbmp 
= new wxBitmap(collapse_xpm
); 
 442     // calculate average font height for bitmap centering 
 444     m_iconWidth 
= m_expandbmp
->GetWidth(); 
 445     m_iconHeight 
= m_expandbmp
->GetHeight(); 
 448     m_curcursor 
= wxCURSOR_ARROW
; 
 449     m_cursorSizeWE 
= new wxCursor( wxCURSOR_SIZEWE 
); 
 451     // adjust bitmap icon y position so they are centered 
 452     m_vspacing 
= wxPG_DEFAULT_VSPACING
; 
 454     CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING 
); 
 456     // Allocate cell datas 
 457     m_propertyDefaultCell
.SetEmptyData(); 
 458     m_categoryDefaultCell
.SetEmptyData(); 
 462     // This helps with flicker 
 463     SetBackgroundStyle( wxBG_STYLE_CUSTOM 
); 
 465     // Hook the top-level parent 
 470     // set virtual size to this window size 
 471     wxSize wndsize 
= GetSize(); 
 472     SetVirtualSize(wndsize
.GetWidth(), wndsize
.GetWidth()); 
 474     m_timeCreated 
= ::wxGetLocalTimeMillis(); 
 476     m_iFlags 
|= wxPG_FL_INITIALIZED
; 
 478     m_ncWidth 
= wndsize
.GetWidth(); 
 480     // Need to call OnResize handler or size given in constructor/Create 
 482     wxSizeEvent 
sizeEvent(wndsize
,0); 
 486 // ----------------------------------------------------------------------- 
 488 wxPropertyGrid::~wxPropertyGrid() 
 493     wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
 497     // Remove grid and property pointers from live wxPropertyGridEvents. 
 498     for ( i
=0; i
<m_liveEvents
.size(); i
++ ) 
 500         wxPropertyGridEvent
* evt 
= m_liveEvents
[i
]; 
 501         evt
->SetPropertyGrid(NULL
); 
 502         evt
->SetProperty(NULL
); 
 504     m_liveEvents
.clear(); 
 506     if ( m_processedEvent 
) 
 508         // All right... we are being deleted while wxPropertyGrid event 
 509         // is being sent. Make sure that event propagates as little 
 510         // as possible (although usually this is not enough to prevent 
 512         m_processedEvent
->Skip(false); 
 513         m_processedEvent
->StopPropagation(); 
 515         // Let's use wxMessageBox to make the message appear more 
 516         // reliably (and *before* the crash can happen). 
 517         ::wxMessageBox("wxPropertyGrid was being destroyed in an event " 
 518                        "generated by it. This usually leads to a crash " 
 519                        "so it is recommended to destroy the control " 
 520                        "at idle time instead."); 
 523     DoSelectProperty(NULL
, wxPG_SEL_NOVALIDATE
|wxPG_SEL_DONT_SEND_EVENT
); 
 525     // This should do prevent things from going too badly wrong 
 526     m_iFlags 
&= ~(wxPG_FL_INITIALIZED
); 
 528     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
 531     // Call with NULL to disconnect event handling 
 532     if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING 
) 
 536         wxASSERT_MSG( !IsEditorsValueModified(), 
 537                       wxS("Most recent change in property editor was ") 
 538                       wxS("lost!!! (if you don't want this to happen, ") 
 539                       wxS("close your frames and dialogs using ") 
 540                       wxS("Close(false).)") ); 
 543 #if wxPG_DOUBLE_BUFFER 
 544     if ( m_doubleBuffer 
) 
 545         delete m_doubleBuffer
; 
 548     if ( m_iFlags 
& wxPG_FL_CREATEDSTATE 
) 
 551     delete m_cursorSizeWE
; 
 553 #ifndef wxPG_ICON_WIDTH 
 558     // Delete common value records 
 559     for ( i
=0; i
<m_commonValues
.size(); i
++ ) 
 561         // Use temporary variable to work around possible strange VC6 (asserts because m_size is zero) 
 562         wxPGCommonValue
* value 
= m_commonValues
[i
]; 
 567 // ----------------------------------------------------------------------- 
 569 bool wxPropertyGrid::Destroy() 
 571     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
 574     return wxControl::Destroy(); 
 577 // ----------------------------------------------------------------------- 
 579 wxPropertyGridPageState
* wxPropertyGrid::CreateState() const 
 581     return new wxPropertyGridPageState(); 
 584 // ----------------------------------------------------------------------- 
 585 // wxPropertyGrid overridden wxWindow methods 
 586 // ----------------------------------------------------------------------- 
 588 void wxPropertyGrid::SetWindowStyleFlag( long style 
) 
 590     long old_style 
= m_windowStyle
; 
 592     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
 594         wxASSERT( m_pState 
); 
 596         if ( !(style 
& wxPG_HIDE_CATEGORIES
) && (old_style 
& wxPG_HIDE_CATEGORIES
) ) 
 599             EnableCategories( true ); 
 601         else if ( (style 
& wxPG_HIDE_CATEGORIES
) && !(old_style 
& wxPG_HIDE_CATEGORIES
) ) 
 603         // Disable categories 
 604             EnableCategories( false ); 
 606         if ( !(old_style 
& wxPG_AUTO_SORT
) && (style 
& wxPG_AUTO_SORT
) ) 
 612                 PrepareAfterItemsAdded(); 
 614                 m_pState
->m_itemsAdded 
= 1; 
 616     #if wxPG_SUPPORT_TOOLTIPS 
 617         if ( !(old_style 
& wxPG_TOOLTIPS
) && (style 
& wxPG_TOOLTIPS
) ) 
 623             wxToolTip* tooltip = new wxToolTip ( wxEmptyString ); 
 624             SetToolTip ( tooltip ); 
 625             tooltip->SetDelay ( wxPG_TOOLTIP_DELAY ); 
 628         else if ( (old_style 
& wxPG_TOOLTIPS
) && !(style 
& wxPG_TOOLTIPS
) ) 
 638     wxControl::SetWindowStyleFlag ( style 
); 
 640     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
 642         if ( (old_style 
& wxPG_HIDE_MARGIN
) != (style 
& wxPG_HIDE_MARGIN
) ) 
 644             CalculateFontAndBitmapStuff( m_vspacing 
); 
 650 // ----------------------------------------------------------------------- 
 652 void wxPropertyGrid::Freeze() 
 661 // ----------------------------------------------------------------------- 
 663 void wxPropertyGrid::Thaw() 
 670         RecalculateVirtualSize(); 
 671     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
 675         // Force property re-selection 
 676         // NB: We must copy the selection. 
 677         wxArrayPGProperty selection 
= m_pState
->m_selection
; 
 678         DoSetSelection(selection
, wxPG_SEL_FORCE 
| wxPG_SEL_NONVISIBLE
); 
 682 // ----------------------------------------------------------------------- 
 684 bool wxPropertyGrid::DoAddToSelection( wxPGProperty
* prop
, int selFlags 
) 
 686     wxCHECK( prop
, false ); 
 688     if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION
) ) 
 689         return DoSelectProperty(prop
, selFlags
); 
 691     wxArrayPGProperty
& selection 
= m_pState
->m_selection
; 
 693     if ( !selection
.size() ) 
 695         return DoSelectProperty(prop
, selFlags
); 
 699         // For categories, only one can be selected at a time 
 700         if ( prop
->IsCategory() || selection
[0]->IsCategory() ) 
 703         selection
.push_back(prop
); 
 705         if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 707             SendEvent( wxEVT_PG_SELECTED
, prop
, NULL 
); 
 716 // ----------------------------------------------------------------------- 
 718 bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty
* prop
, int selFlags 
) 
 720     wxCHECK( prop
, false ); 
 723     wxArrayPGProperty
& selection 
= m_pState
->m_selection
; 
 724     if ( selection
.size() <= 1 ) 
 726         res 
= DoSelectProperty(NULL
, selFlags
); 
 730         m_pState
->DoRemoveFromSelection(prop
); 
 738 // ----------------------------------------------------------------------- 
 740 bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty
* prop
, 
 741                                       unsigned int colIndex
, 
 742                                       unsigned int selFlags 
) 
 745     // NB: Enable following if label editor background colour is 
 746     //     ever changed to any other than m_colSelBack. 
 748     // We use this workaround to prevent visible flicker when editing 
 749     // a cell. Atleast on wxMSW, there is a difficult to find 
 750     // (and perhaps prevent) redraw somewhere between making property 
 751     // selected and enabling label editing. 
 753     //wxColour prevColSelBack = m_colSelBack; 
 754     //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); 
 760         res 
= DoSelectProperty(prop
, selFlags
); 
 765         DoClearSelection(false, wxPG_SEL_NO_REFRESH
); 
 767         if ( m_pState
->m_editableColumns
.Index(colIndex
) == wxNOT_FOUND 
) 
 769             res 
= DoAddToSelection(prop
, selFlags
); 
 773             res 
= DoAddToSelection(prop
, selFlags
|wxPG_SEL_NO_REFRESH
); 
 775             DoBeginLabelEdit(colIndex
, selFlags
); 
 779     //m_colSelBack = prevColSelBack; 
 783 // ----------------------------------------------------------------------- 
 785 bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty
* prop
, 
 786                                                    unsigned int colIndex
, 
 787                                                    wxMouseEvent
* mouseEvent
, 
 790     const wxArrayPGProperty
& selection 
= GetSelectedProperties(); 
 791     bool alreadySelected 
= m_pState
->DoIsPropertySelected(prop
); 
 794     // Set to 2 if also add all items in between 
 795     int addToExistingSelection 
= 0; 
 797     if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION 
) 
 801             if ( mouseEvent
->GetEventType() == wxEVT_RIGHT_DOWN 
|| 
 802                  mouseEvent
->GetEventType() == wxEVT_RIGHT_UP 
) 
 804                 // Allow right-click for context menu without 
 805                 // disturbing the selection. 
 806                 if ( GetSelectedProperties().size() <= 1 || 
 808                     return DoSelectAndEdit(prop
, colIndex
, selFlags
); 
 813                 if ( mouseEvent
->ControlDown() ) 
 815                     addToExistingSelection 
= 1; 
 817                 else if ( mouseEvent
->ShiftDown() ) 
 819                     if ( selection
.size() > 0 && !prop
->IsCategory() ) 
 820                         addToExistingSelection 
= 2; 
 822                         addToExistingSelection 
= 1; 
 828     if ( addToExistingSelection 
== 1 ) 
 831         if ( !alreadySelected 
) 
 833             res 
= DoAddToSelection(prop
, selFlags
); 
 835         else if ( GetSelectedProperties().size() > 1 ) 
 837             res 
= DoRemoveFromSelection(prop
, selFlags
); 
 840     else if ( addToExistingSelection 
== 2 ) 
 842         // Add this, and all in between 
 844         // Find top selected property 
 845         wxPGProperty
* topSelProp 
= selection
[0]; 
 846         int topSelPropY 
= topSelProp
->GetY(); 
 847         for ( unsigned int i
=1; i
<selection
.size(); i
++ ) 
 849             wxPGProperty
* p 
= selection
[i
]; 
 851             if ( y 
< topSelPropY 
) 
 858         wxPGProperty
* startFrom
; 
 859         wxPGProperty
* stopAt
; 
 861         if ( prop
->GetY() <= topSelPropY 
) 
 863             // Property is above selection (or same) 
 869             // Property is below selection 
 870             startFrom 
= topSelProp
; 
 874         // Iterate through properties in-between, and select them 
 875         wxPropertyGridIterator it
; 
 877         for ( it 
= GetIterator(wxPG_ITERATE_VISIBLE
, startFrom
); 
 881             wxPGProperty
* p 
= *it
; 
 883             if ( !p
->IsCategory() && 
 884                  !m_pState
->DoIsPropertySelected(p
) ) 
 886                 DoAddToSelection(p
, selFlags
); 
 895         res 
= DoSelectAndEdit(prop
, colIndex
, selFlags
); 
 901 // ----------------------------------------------------------------------- 
 903 void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty
& newSelection
, 
 906     if ( newSelection
.size() > 0 ) 
 908         if ( !DoSelectProperty(newSelection
[0], selFlags
) ) 
 913         DoClearSelection(false, selFlags
); 
 916     for ( unsigned int i 
= 1; i 
< newSelection
.size(); i
++ ) 
 918         DoAddToSelection(newSelection
[i
], selFlags
); 
 924 // ----------------------------------------------------------------------- 
 926 void wxPropertyGrid::MakeColumnEditable( unsigned int column
, 
 929     wxASSERT( column 
!= 1 ); 
 931     wxArrayInt
& cols 
= m_pState
->m_editableColumns
; 
 935         cols
.push_back(column
); 
 939         for ( int i 
= cols
.size() - 1; i 
> 0; i
-- ) 
 941             if ( cols
[i
] == (int)column 
) 
 942                 cols
.erase( cols
.begin() + i 
); 
 947 // ----------------------------------------------------------------------- 
 949 void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex
, 
 952     wxPGProperty
* selected 
= GetSelection(); 
 953     wxCHECK_RET(selected
, wxT("No property selected")); 
 954     wxCHECK_RET(colIndex 
!= 1, wxT("Do not use this for column 1")); 
 956     if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 958         if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN
, 
 965     const wxPGCell
* cell 
= NULL
; 
 966     if ( selected
->HasCell(colIndex
) ) 
 968         cell 
= &selected
->GetCell(colIndex
); 
 969         if ( !cell
->HasText() && colIndex 
== 0 ) 
 970             text 
= selected
->GetLabel(); 
 976             text 
= selected
->GetLabel(); 
 978             cell 
= &selected
->GetOrCreateCell(colIndex
); 
 981     if ( cell 
&& cell
->HasText() ) 
 982         text 
= cell
->GetText(); 
 984     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
);  // send event 
 986     m_selColumn 
= colIndex
; 
 988     wxRect r 
= GetEditorWidgetRect(selected
, m_selColumn
); 
 990     wxWindow
* tc 
= GenerateEditorTextCtrl(r
.GetPosition(), 
 998     wxWindowID id 
= tc
->GetId(); 
 999     tc
->Connect(id
, wxEVT_COMMAND_TEXT_ENTER
, 
1000         wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress
), 
1002     tc
->Connect(id
, wxEVT_KEY_DOWN
, 
1003         wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress
), 
1008     m_labelEditor 
= wxStaticCast(tc
, wxTextCtrl
); 
1009     m_labelEditorProperty 
= selected
; 
1012 // ----------------------------------------------------------------------- 
1015 wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent
& WXUNUSED(event
) ) 
1017     DoEndLabelEdit(true); 
1020 // ----------------------------------------------------------------------- 
1022 void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent
& event 
) 
1024     int keycode 
= event
.GetKeyCode(); 
1026     if ( keycode 
== WXK_ESCAPE 
) 
1028         DoEndLabelEdit(false); 
1032         HandleKeyEvent(event
, true); 
1036 // ----------------------------------------------------------------------- 
1038 void wxPropertyGrid::DoEndLabelEdit( bool commit
, int selFlags 
) 
1040     if ( !m_labelEditor 
) 
1043     wxPGProperty
* prop 
= m_labelEditorProperty
; 
1048         if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
1050             // wxPG_SEL_NOVALIDATE is passed correctly in selFlags 
1051             if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING
, 
1052                             prop
, NULL
, selFlags
, 
1057         wxString text 
= m_labelEditor
->GetValue(); 
1058         wxPGCell
* cell 
= NULL
; 
1059         if ( prop
->HasCell(m_selColumn
) ) 
1061             cell 
= &prop
->GetCell(m_selColumn
); 
1065             if ( m_selColumn 
== 0 ) 
1066                 prop
->SetLabel(text
); 
1068                 cell 
= &prop
->GetOrCreateCell(m_selColumn
); 
1072             cell
->SetText(text
); 
1076     int wasFocused 
= m_iFlags 
& wxPG_FL_FOCUSED
; 
1078     DestroyEditorWnd(m_labelEditor
); 
1080     m_labelEditor 
= NULL
; 
1081     m_labelEditorProperty 
= NULL
; 
1083     // Fix focus (needed at least on wxGTK) 
1090 // ----------------------------------------------------------------------- 
1092 void wxPropertyGrid::SetExtraStyle( long exStyle 
) 
1094     if ( exStyle 
& wxPG_EX_ENABLE_TLP_TRACKING 
) 
1095         OnTLPChanging(::wxGetTopLevelParent(this)); 
1097         OnTLPChanging(NULL
); 
1099     if ( exStyle 
& wxPG_EX_NATIVE_DOUBLE_BUFFERING 
) 
1101 #if defined(__WXMSW__) 
1104         // Don't use WS_EX_COMPOSITED just now. 
1107         if ( m_iFlags & wxPG_FL_IN_MANAGER ) 
1108             hWnd = (HWND)GetParent()->GetHWND(); 
1110             hWnd = (HWND)GetHWND(); 
1112         ::SetWindowLong( hWnd, GWL_EXSTYLE, 
1113                          ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED ); 
1116 //#elif defined(__WXGTK20__) 
1118         // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window 
1119         // truly was double-buffered. 
1120         if ( !this->IsDoubleBuffered() ) 
1122             exStyle 
&= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING
); 
1126         #if wxPG_DOUBLE_BUFFER 
1127             wxDELETE(m_doubleBuffer
); 
1132     wxControl::SetExtraStyle( exStyle 
); 
1134     if ( exStyle 
& wxPG_EX_INIT_NOCAT 
) 
1135         m_pState
->InitNonCatMode(); 
1137     if ( exStyle 
& wxPG_EX_HELP_AS_TOOLTIPS 
) 
1138         m_windowStyle 
|= wxPG_TOOLTIPS
; 
1141     wxPGGlobalVars
->m_extraStyle 
= exStyle
; 
1144 // ----------------------------------------------------------------------- 
1146 // returns the best acceptable minimal size 
1147 wxSize 
wxPropertyGrid::DoGetBestSize() const 
1149     int lineHeight 
= wxMax(15, m_lineHeight
); 
1151     // don't make the grid too tall (limit height to 10 items) but don't 
1152     // make it too small neither 
1153     int numLines 
= wxMin
 
1155                     wxMax(m_pState
->m_properties
->GetChildCount(), 3), 
1159     wxClientDC 
dc(const_cast<wxPropertyGrid 
*>(this)); 
1160     int width 
= m_marginWidth
; 
1161     for ( unsigned int i 
= 0; i 
< m_pState
->m_colWidths
.size(); i
++ ) 
1163         width 
+= m_pState
->GetColumnFitWidth(dc
, m_pState
->DoGetRoot(), i
, true); 
1166     const wxSize sz 
= wxSize(width
, lineHeight
*numLines 
+ 40); 
1172 // ----------------------------------------------------------------------- 
1174 void wxPropertyGrid::OnTLPChanging( wxWindow
* newTLP 
) 
1176     if ( newTLP 
== m_tlp 
) 
1179     wxLongLong currentTime 
= ::wxGetLocalTimeMillis(); 
1182     // Parent changed so let's redetermine and re-hook the 
1183     // correct top-level window. 
1186         m_tlp
->Disconnect( wxEVT_CLOSE_WINDOW
, 
1187                            wxCloseEventHandler(wxPropertyGrid::OnTLPClose
), 
1189         m_tlpClosed 
= m_tlp
; 
1190         m_tlpClosedTime 
= currentTime
; 
1195         // Only accept new tlp if same one was not just dismissed. 
1196         if ( newTLP 
!= m_tlpClosed 
|| 
1197              m_tlpClosedTime
+250 < currentTime 
) 
1199             newTLP
->Connect( wxEVT_CLOSE_WINDOW
, 
1200                              wxCloseEventHandler(wxPropertyGrid::OnTLPClose
), 
1213 // ----------------------------------------------------------------------- 
1215 void wxPropertyGrid::OnTLPClose( wxCloseEvent
& event 
) 
1217     // ClearSelection forces value validation/commit. 
1218     if ( event
.CanVeto() && !DoClearSelection() ) 
1224     // Ok, it can close, set tlp pointer to NULL. Some other event 
1225     // handler can of course veto the close, but our OnIdle() should 
1226     // then be able to regain the tlp pointer. 
1227     OnTLPChanging(NULL
); 
1232 // ----------------------------------------------------------------------- 
1234 bool wxPropertyGrid::Reparent( wxWindowBase 
*newParent 
) 
1236     OnTLPChanging((wxWindow
*)newParent
); 
1238     bool res 
= wxControl::Reparent(newParent
); 
1243 // ----------------------------------------------------------------------- 
1244 // wxPropertyGrid Font and Colour Methods 
1245 // ----------------------------------------------------------------------- 
1247 void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing 
) 
1251     m_captionFont 
= wxControl::GetFont(); 
1253     GetTextExtent(wxS("jG"), &x
, &y
, 0, 0, &m_captionFont
); 
1254     m_subgroup_extramargin 
= x 
+ (x
/2); 
1257 #if wxPG_USE_RENDERER_NATIVE 
1258     m_iconWidth 
= wxPG_ICON_WIDTH
; 
1259 #elif wxPG_ICON_WIDTH 
1261     m_iconWidth 
= (m_fontHeight 
* wxPG_ICON_WIDTH
) / 13; 
1262     if ( m_iconWidth 
< 5 ) m_iconWidth 
= 5; 
1263     else if ( !(m_iconWidth 
& 0x01) ) m_iconWidth
++; // must be odd 
1267     m_gutterWidth 
= m_iconWidth 
/ wxPG_GUTTER_DIV
; 
1268     if ( m_gutterWidth 
< wxPG_GUTTER_MIN 
) 
1269         m_gutterWidth 
= wxPG_GUTTER_MIN
; 
1272     if ( vspacing 
<= 1 ) vdiv 
= 12; 
1273     else if ( vspacing 
>= 3 ) vdiv 
= 3; 
1275     m_spacingy 
= m_fontHeight 
/ vdiv
; 
1276     if ( m_spacingy 
< wxPG_YSPACING_MIN 
) 
1277         m_spacingy 
= wxPG_YSPACING_MIN
; 
1280     if ( !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
1281         m_marginWidth 
= m_gutterWidth
*2 + m_iconWidth
; 
1283     m_captionFont
.SetWeight(wxBOLD
); 
1284     GetTextExtent(wxS("jG"), &x
, &y
, 0, 0, &m_captionFont
); 
1286     m_lineHeight 
= m_fontHeight
+(2*m_spacingy
)+1; 
1289     m_buttonSpacingY 
= (m_lineHeight 
- m_iconHeight
) / 2; 
1290     if ( m_buttonSpacingY 
< 0 ) m_buttonSpacingY 
= 0; 
1293         m_pState
->CalculateFontAndBitmapStuff(vspacing
); 
1295     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
1296         RecalculateVirtualSize(); 
1298     InvalidateBestSize(); 
1301 // ----------------------------------------------------------------------- 
1303 void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent 
&WXUNUSED(event
) ) 
1309 // ----------------------------------------------------------------------- 
1311 static wxColour 
wxPGAdjustColour(const wxColour
& src
, int ra
, 
1312                                  int ga 
= 1000, int ba 
= 1000, 
1313                                  bool forceDifferent 
= false) 
1320     // Recursion guard (allow 2 max) 
1321     static int isinside 
= 0; 
1323     wxCHECK_MSG( isinside 
< 3, 
1325                  wxT("wxPGAdjustColour should not be recursively called more than once") ); 
1330     int g 
= src
.Green(); 
1333     if ( r2
>255 ) r2 
= 255; 
1334     else if ( r2
<0) r2 
= 0; 
1336     if ( g2
>255 ) g2 
= 255; 
1337     else if ( g2
<0) g2 
= 0; 
1339     if ( b2
>255 ) b2 
= 255; 
1340     else if ( b2
<0) b2 
= 0; 
1342     // Make sure they are somewhat different 
1343     if ( forceDifferent 
&& (abs((r
+g
+b
)-(r2
+g2
+b2
)) < abs(ra
/2)) ) 
1344         dst 
= wxPGAdjustColour(src
,-(ra
*2)); 
1346         dst 
= wxColour(r2
,g2
,b2
); 
1348     // Recursion guard (allow 2 max) 
1355 static int wxPGGetColAvg( const wxColour
& col 
) 
1357     return (col
.Red() + col
.Green() + col
.Blue()) / 3; 
1361 void wxPropertyGrid::RegainColours() 
1363     if ( !(m_coloursCustomized 
& 0x0002) ) 
1365         wxColour col 
= wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE 
); 
1367         // Make sure colour is dark enough 
1369         int colDec 
= wxPGGetColAvg(col
) - 230; 
1371         int colDec 
= wxPGGetColAvg(col
) - 200; 
1374             m_colCapBack 
= wxPGAdjustColour(col
,-colDec
); 
1377         m_categoryDefaultCell
.GetData()->SetBgCol(m_colCapBack
); 
1380     if ( !(m_coloursCustomized 
& 0x0001) ) 
1381         m_colMargin 
= m_colCapBack
; 
1383     if ( !(m_coloursCustomized 
& 0x0004) ) 
1390         wxColour capForeCol 
= wxPGAdjustColour(m_colCapBack
,colDec
,5000,5000,true); 
1391         m_colCapFore 
= capForeCol
; 
1392         m_categoryDefaultCell
.GetData()->SetFgCol(capForeCol
); 
1395     if ( !(m_coloursCustomized 
& 0x0008) ) 
1397         wxColour bgCol 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW 
); 
1398         m_colPropBack 
= bgCol
; 
1399         m_propertyDefaultCell
.GetData()->SetBgCol(bgCol
); 
1400         if ( !m_unspecifiedAppearance
.GetBgCol().IsOk() ) 
1401             m_unspecifiedAppearance
.SetBgCol(bgCol
); 
1404     if ( !(m_coloursCustomized 
& 0x0010) ) 
1406         wxColour fgCol 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT 
); 
1407         m_colPropFore 
= fgCol
; 
1408         m_propertyDefaultCell
.GetData()->SetFgCol(fgCol
); 
1409         if ( !m_unspecifiedAppearance
.GetFgCol().IsOk() ) 
1410             m_unspecifiedAppearance
.SetFgCol(fgCol
); 
1413     if ( !(m_coloursCustomized 
& 0x0020) ) 
1414         m_colSelBack 
= wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT 
); 
1416     if ( !(m_coloursCustomized 
& 0x0040) ) 
1417         m_colSelFore 
= wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT 
); 
1419     if ( !(m_coloursCustomized 
& 0x0080) ) 
1420         m_colLine 
= m_colCapBack
; 
1422     if ( !(m_coloursCustomized 
& 0x0100) ) 
1423         m_colDisPropFore 
= m_colCapFore
; 
1425     m_colEmptySpace 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW 
); 
1428 // ----------------------------------------------------------------------- 
1430 void wxPropertyGrid::ResetColours() 
1432     m_coloursCustomized 
= 0; 
1439 // ----------------------------------------------------------------------- 
1441 bool wxPropertyGrid::SetFont( const wxFont
& font 
) 
1443     // Must disable active editor. 
1446     bool res 
= wxControl::SetFont( font 
); 
1447     if ( res 
&& GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant 
1449         CalculateFontAndBitmapStuff( m_vspacing 
); 
1456 // ----------------------------------------------------------------------- 
1458 void wxPropertyGrid::SetLineColour( const wxColour
& col 
) 
1461     m_coloursCustomized 
|= 0x80; 
1465 // ----------------------------------------------------------------------- 
1467 void wxPropertyGrid::SetMarginColour( const wxColour
& col 
) 
1470     m_coloursCustomized 
|= 0x01; 
1474 // ----------------------------------------------------------------------- 
1476 void wxPropertyGrid::SetCellBackgroundColour( const wxColour
& col 
) 
1478     m_colPropBack 
= col
; 
1479     m_coloursCustomized 
|= 0x08; 
1481     m_propertyDefaultCell
.GetData()->SetBgCol(col
); 
1482     m_unspecifiedAppearance
.SetBgCol(col
); 
1487 // ----------------------------------------------------------------------- 
1489 void wxPropertyGrid::SetCellTextColour( const wxColour
& col 
) 
1491     m_colPropFore 
= col
; 
1492     m_coloursCustomized 
|= 0x10; 
1494     m_propertyDefaultCell
.GetData()->SetFgCol(col
); 
1495     m_unspecifiedAppearance
.SetFgCol(col
); 
1500 // ----------------------------------------------------------------------- 
1502 void wxPropertyGrid::SetEmptySpaceColour( const wxColour
& col 
) 
1504     m_colEmptySpace 
= col
; 
1509 // ----------------------------------------------------------------------- 
1511 void wxPropertyGrid::SetCellDisabledTextColour( const wxColour
& col 
) 
1513     m_colDisPropFore 
= col
; 
1514     m_coloursCustomized 
|= 0x100; 
1518 // ----------------------------------------------------------------------- 
1520 void wxPropertyGrid::SetSelectionBackgroundColour( const wxColour
& col 
) 
1523     m_coloursCustomized 
|= 0x20; 
1527 // ----------------------------------------------------------------------- 
1529 void wxPropertyGrid::SetSelectionTextColour( const wxColour
& col 
) 
1532     m_coloursCustomized 
|= 0x40; 
1536 // ----------------------------------------------------------------------- 
1538 void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour
& col 
) 
1541     m_coloursCustomized 
|= 0x02; 
1543     m_categoryDefaultCell
.GetData()->SetBgCol(col
); 
1548 // ----------------------------------------------------------------------- 
1550 void wxPropertyGrid::SetCaptionTextColour( const wxColour
& col 
) 
1553     m_coloursCustomized 
|= 0x04; 
1555     m_categoryDefaultCell
.GetData()->SetFgCol(col
); 
1560 // ----------------------------------------------------------------------- 
1561 // wxPropertyGrid property adding and removal 
1562 // ----------------------------------------------------------------------- 
1564 void wxPropertyGrid::PrepareAfterItemsAdded() 
1566     if ( !m_pState 
|| !m_pState
->m_itemsAdded 
) return; 
1568     m_pState
->m_itemsAdded 
= 0; 
1570     if ( m_windowStyle 
& wxPG_AUTO_SORT 
) 
1571         Sort(wxPG_SORT_TOP_LEVEL_ONLY
); 
1573     RecalculateVirtualSize(); 
1575     // Fix editor position 
1576     CorrectEditorWidgetPosY(); 
1579 // ----------------------------------------------------------------------- 
1580 // wxPropertyGrid property operations 
1581 // ----------------------------------------------------------------------- 
1583 bool wxPropertyGrid::EnsureVisible( wxPGPropArg id 
) 
1585     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
1589     bool changed 
= false; 
1591     // Is it inside collapsed section? 
1592     if ( !p
->IsVisible() ) 
1595         wxPGProperty
* parent 
= p
->GetParent(); 
1596         wxPGProperty
* grandparent 
= parent
->GetParent(); 
1598         if ( grandparent 
&& grandparent 
!= m_pState
->m_properties 
) 
1599             Expand( grandparent 
); 
1607     GetViewStart(&vx
,&vy
); 
1608     vy
*=wxPG_PIXELS_PER_UNIT
; 
1614         Scroll(vx
, y
/wxPG_PIXELS_PER_UNIT 
); 
1615         m_iFlags 
|= wxPG_FL_SCROLLED
; 
1618     else if ( (y
+m_lineHeight
) > (vy
+m_height
) ) 
1620         Scroll(vx
, (y
-m_height
+(m_lineHeight
*2))/wxPG_PIXELS_PER_UNIT 
); 
1621         m_iFlags 
|= wxPG_FL_SCROLLED
; 
1631 // ----------------------------------------------------------------------- 
1632 // wxPropertyGrid helper methods called by properties 
1633 // ----------------------------------------------------------------------- 
1635 // Control font changer helper. 
1636 void wxPropertyGrid::SetCurControlBoldFont() 
1638     wxWindow
* editor 
= GetEditorControl(); 
1639     editor
->SetFont( m_captionFont 
); 
1642 // ----------------------------------------------------------------------- 
1644 wxPoint 
wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty
* p
, 
1647 #if wxPG_SMALL_SCREEN 
1648     // On small-screen devices, always show dialogs with default position and size. 
1649     return wxDefaultPosition
; 
1651     int splitterX 
= GetSplitterPosition(); 
1655     wxCHECK_MSG( y 
>= 0, wxPoint(-1,-1), wxT("invalid y?") ); 
1657     ImprovedClientToScreen( &x
, &y 
); 
1659     int sw 
= wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X 
); 
1660     int sh 
= wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y 
); 
1667         new_x 
= x 
+ (m_width
-splitterX
) - sz
.x
; 
1677         new_y 
= y 
+ m_lineHeight
; 
1679     return wxPoint(new_x
,new_y
); 
1683 // ----------------------------------------------------------------------- 
1685 wxString
& wxPropertyGrid::ExpandEscapeSequences( wxString
& dst_str
, wxString
& src_str 
) 
1687     if ( src_str
.length() == 0 ) 
1693     bool prev_is_slash 
= false; 
1695     wxString::const_iterator i 
= src_str
.begin(); 
1699     for ( ; i 
!= src_str
.end(); ++i 
) 
1703         if ( a 
!= wxS('\\') ) 
1705             if ( !prev_is_slash 
) 
1711                 if ( a 
== wxS('n') ) 
1714                     dst_str 
<< wxS('\n'); 
1716                     dst_str 
<< wxS('\n'); 
1719                 else if ( a 
== wxS('t') ) 
1720                     dst_str 
<< wxS('\t'); 
1724             prev_is_slash 
= false; 
1728             if ( prev_is_slash 
) 
1730                 dst_str 
<< wxS('\\'); 
1731                 prev_is_slash 
= false; 
1735                 prev_is_slash 
= true; 
1742 // ----------------------------------------------------------------------- 
1744 wxString
& wxPropertyGrid::CreateEscapeSequences( wxString
& dst_str
, wxString
& src_str 
) 
1746     if ( src_str
.length() == 0 ) 
1752     wxString::const_iterator i 
= src_str
.begin(); 
1753     wxUniChar prev_a 
= wxS('\0'); 
1757     for ( ; i 
!= src_str
.end(); ++i 
) 
1761         if ( a 
>= wxS(' ') ) 
1763             // This surely is not something that requires an escape sequence. 
1768             // This might need... 
1769             if ( a 
== wxS('\r')  ) 
1771                 // DOS style line end. 
1772                 // Already taken care below 
1774             else if ( a 
== wxS('\n') ) 
1775                 // UNIX style line end. 
1776                 dst_str 
<< wxS("\\n"); 
1777             else if ( a 
== wxS('\t') ) 
1779                 dst_str 
<< wxS('\t'); 
1782                 //wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a); 
1792 // ----------------------------------------------------------------------- 
1794 wxPGProperty
* wxPropertyGrid::DoGetItemAtY( int y 
) const 
1801     return m_pState
->m_properties
->GetItemAtY(y
, m_lineHeight
, &a
); 
1804 // ----------------------------------------------------------------------- 
1805 // wxPropertyGrid graphics related methods 
1806 // ----------------------------------------------------------------------- 
1808 void wxPropertyGrid::OnPaint( wxPaintEvent
& WXUNUSED(event
) ) 
1813     // Don't paint after destruction has begun 
1814     if ( !HasInternalFlag(wxPG_FL_INITIALIZED
) ) 
1817     // Find out where the window is scrolled to 
1818     int vx
,vy
;                     // Top left corner of client 
1819     GetViewStart(&vx
,&vy
); 
1820     vy 
*= wxPG_PIXELS_PER_UNIT
; 
1822     // Update everything inside the box 
1823     wxRect r 
= GetUpdateRegion().GetBox(); 
1827     // FIXME: This is just a workaround for a bug that causes splitters not 
1828     //        to paint when other windows are being dragged over the grid. 
1830     r
.width 
= GetClientSize().x
; 
1833     r
.height 
= GetClientSize().y
; 
1835     // Repaint this rectangle 
1836     DrawItems( dc
, r
.y
, r
.y 
+ r
.height
, &r 
); 
1838     // We assume that the size set when grid is shown 
1839     // is what is desired. 
1840     SetInternalFlag(wxPG_FL_GOOD_SIZE_SET
); 
1843 // ----------------------------------------------------------------------- 
1845 void wxPropertyGrid::DrawExpanderButton( wxDC
& dc
, const wxRect
& rect
, 
1846                                          wxPGProperty
* property 
) const 
1848     // Prepare rectangle to be used 
1850     r
.x 
+= m_gutterWidth
; r
.y 
+= m_buttonSpacingY
; 
1851     r
.width 
= m_iconWidth
; r
.height 
= m_iconHeight
; 
1853 #if (wxPG_USE_RENDERER_NATIVE) 
1855 #elif wxPG_ICON_WIDTH 
1856     // Drawing expand/collapse button manually 
1857     dc
.SetPen(m_colPropFore
); 
1858     if ( property
->IsCategory() ) 
1859         dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
1861         dc
.SetBrush(m_colPropBack
); 
1863     dc
.DrawRectangle( r 
); 
1864     int _y 
= r
.y
+(m_iconWidth
/2); 
1865     dc
.DrawLine(r
.x
+2,_y
,r
.x
+m_iconWidth
-2,_y
); 
1870     if ( property
->IsExpanded() ) 
1872     // wxRenderer functions are non-mutating in nature, so it 
1873     // should be safe to cast "const wxPropertyGrid*" to "wxWindow*". 
1874     // Hopefully this does not cause problems. 
1875     #if (wxPG_USE_RENDERER_NATIVE) 
1876         wxRendererNative::Get().DrawTreeItemButton( 
1882     #elif wxPG_ICON_WIDTH 
1891     #if (wxPG_USE_RENDERER_NATIVE) 
1892         wxRendererNative::Get().DrawTreeItemButton( 
1898     #elif wxPG_ICON_WIDTH 
1899         int _x 
= r
.x
+(m_iconWidth
/2); 
1900         dc
.DrawLine(_x
,r
.y
+2,_x
,r
.y
+m_iconWidth
-2); 
1906 #if (wxPG_USE_RENDERER_NATIVE) 
1908 #elif wxPG_ICON_WIDTH 
1911     dc
.DrawBitmap( *bmp
, r
.x
, r
.y
, true ); 
1915 // ----------------------------------------------------------------------- 
1918 // This is the one called by OnPaint event handler and others. 
1919 // topy and bottomy are already unscrolled (ie. physical) 
1921 void wxPropertyGrid::DrawItems( wxDC
& dc
, 
1922                                 unsigned int topItemY
, 
1923                                 unsigned int bottomItemY
, 
1924                                 const wxRect
* itemsRect 
) 
1928          bottomItemY 
< topItemY 
|| 
1932     m_pState
->EnsureVirtualHeight(); 
1934     wxRect tempItemsRect
; 
1937         tempItemsRect 
= wxRect(0, topItemY
, 
1940         itemsRect 
= &tempItemsRect
; 
1944     GetViewStart(&vx
, &vy
); 
1945     vx 
*= wxPG_PIXELS_PER_UNIT
; 
1946     vy 
*= wxPG_PIXELS_PER_UNIT
; 
1948     wxRect 
drawRect(itemsRect
->x 
- vx
, 
1953     // items added check 
1954     if ( m_pState
->m_itemsAdded 
) PrepareAfterItemsAdded(); 
1956     int paintFinishY 
= 0; 
1958     if ( m_pState
->m_properties
->GetChildCount() > 0 ) 
1961         bool isBuffered 
= false; 
1963     #if wxPG_DOUBLE_BUFFER 
1964         wxMemoryDC
* bufferDC 
= NULL
; 
1966         if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING
) ) 
1968             if ( !m_doubleBuffer 
) 
1970                 paintFinishY 
= itemsRect
->y
; 
1975                 bufferDC 
= new wxMemoryDC(); 
1977                 // If nothing was changed, then just copy from double-buffer 
1978                 bufferDC
->SelectObject( *m_doubleBuffer 
); 
1988             paintFinishY 
= DoDrawItems( *dcPtr
, itemsRect
, isBuffered 
); 
1989             int drawBottomY 
= itemsRect
->y 
+ itemsRect
->height
; 
1991             // Clear area beyond last painted property 
1992             if ( paintFinishY 
< drawBottomY 
) 
1994                 wxLogDebug("%i", paintFinishY
); 
1995                 dcPtr
->SetPen(m_colEmptySpace
); 
1996                 dcPtr
->SetBrush(m_colEmptySpace
); 
1997                 dcPtr
->DrawRectangle(0, paintFinishY
, 
2003     #if wxPG_DOUBLE_BUFFER 
2006             dc
.Blit( drawRect
.x
, drawRect
.y
, drawRect
.width
, 
2008                      bufferDC
, 0, 0, wxCOPY 
); 
2015         // Just clear the area 
2016         dc
.SetPen(m_colEmptySpace
); 
2017         dc
.SetBrush(m_colEmptySpace
); 
2018         dc
.DrawRectangle(drawRect
); 
2022 // ----------------------------------------------------------------------- 
2024 int wxPropertyGrid::DoDrawItems( wxDC
& dc
, 
2025                                  const wxRect
* itemsRect
, 
2026                                  bool isBuffered 
) const 
2028     const wxPGProperty
* firstItem
; 
2029     const wxPGProperty
* lastItem
; 
2031     firstItem 
= DoGetItemAtY(itemsRect
->y
); 
2032     lastItem 
= DoGetItemAtY(itemsRect
->y
+itemsRect
->height
-1); 
2035         lastItem 
= GetLastItem( wxPG_ITERATE_VISIBLE 
); 
2037     if ( m_frozen 
|| m_height 
< 1 || firstItem 
== NULL 
) 
2038         return itemsRect
->y
; 
2040     wxCHECK_MSG( !m_pState
->m_itemsAdded
, itemsRect
->y
, 
2042     wxASSERT( m_pState
->m_properties
->GetChildCount() ); 
2044     int lh 
= m_lineHeight
; 
2047     int lastItemBottomY
; 
2049     firstItemTopY 
= itemsRect
->y
; 
2050     lastItemBottomY 
= itemsRect
->y 
+ itemsRect
->height
; 
2052     // Align y coordinates to item boundaries 
2053     firstItemTopY 
-= firstItemTopY 
% lh
; 
2054     lastItemBottomY 
+= lh 
- (lastItemBottomY 
% lh
); 
2055     lastItemBottomY 
-= 1; 
2057     // Entire range outside scrolled, visible area? 
2058     if ( firstItemTopY 
>= (int)m_pState
->GetVirtualHeight() || 
2059          lastItemBottomY 
<= 0 ) 
2060         return itemsRect
->y
; 
2062     wxCHECK_MSG( firstItemTopY 
< lastItemBottomY
, 
2064                  "invalid y values" ); 
2067     wxLogDebug("  -> DoDrawItems ( \"%s\" -> \"%s\" 
2068                "height=%i (ch=%i), itemsRect = 0x%lX )", 
2069         firstItem->GetLabel().c_str(), 
2070         lastItem->GetLabel().c_str(), 
2071         (int)(lastItemBottomY - firstItemTopY), 
2073         (unsigned long)&itemsRect ); 
2078     long windowStyle 
= m_windowStyle
; 
2083     // With wxPG_DOUBLE_BUFFER, do double buffering 
2084     // - buffer's y = 0, so align itemsRect and coordinates to that 
2086 #if wxPG_DOUBLE_BUFFER 
2093         xRelMod 
= itemsRect
->x
; 
2094         yRelMod 
= itemsRect
->y
; 
2097         // itemsRect conversion 
2102         firstItemTopY 
-= yRelMod
; 
2103         lastItemBottomY 
-= yRelMod
; 
2106     wxUnusedVar(isBuffered
); 
2109     int x 
= m_marginWidth 
- xRelMod
; 
2111     wxFont normalFont 
= GetFont(); 
2113     bool reallyFocused 
= (m_iFlags 
& wxPG_FL_FOCUSED
) != 0; 
2115     bool isPgEnabled 
= IsEnabled(); 
2118     // Prepare some pens and brushes that are often changed to. 
2121     wxBrush 
marginBrush(m_colMargin
); 
2122     wxPen 
marginPen(m_colMargin
); 
2123     wxBrush 
capbgbrush(m_colCapBack
,wxSOLID
); 
2124     wxPen 
linepen(m_colLine
,1,wxSOLID
); 
2126     wxColour selBackCol
; 
2128         selBackCol 
= m_colSelBack
; 
2130         selBackCol 
= m_colMargin
; 
2132     // pen that has same colour as text 
2133     wxPen 
outlinepen(m_colPropFore
,1,wxSOLID
); 
2136     // Clear margin with background colour 
2138     dc
.SetBrush( marginBrush 
); 
2139     if ( !(windowStyle 
& wxPG_HIDE_MARGIN
) ) 
2141         dc
.SetPen( *wxTRANSPARENT_PEN 
); 
2142         dc
.DrawRectangle(-1-xRelMod
,firstItemTopY
-1,x
+2,lastItemBottomY
-firstItemTopY
+2); 
2145     const wxPGProperty
* firstSelected 
= GetSelection(); 
2146     const wxPropertyGridPageState
* state 
= m_pState
; 
2147     const wxArrayInt
& colWidths 
= state
->m_colWidths
; 
2149 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2150     bool wasSelectedPainted 
= false; 
2153     // TODO: Only render columns that are within clipping region. 
2155     dc
.SetFont(normalFont
); 
2157     wxPropertyGridConstIterator 
it( state
, wxPG_ITERATE_VISIBLE
, firstItem 
); 
2158     int endScanBottomY 
= lastItemBottomY 
+ lh
; 
2159     int y 
= firstItemTopY
; 
2162     // Pregenerate list of visible properties. 
2163     wxArrayPGProperty visPropArray
; 
2164     visPropArray
.reserve((m_height
/m_lineHeight
)+6); 
2166     for ( ; !it
.AtEnd(); it
.Next() ) 
2168         const wxPGProperty
* p 
= *it
; 
2170         if ( !p
->HasFlag(wxPG_PROP_HIDDEN
) ) 
2172             visPropArray
.push_back((wxPGProperty
*)p
); 
2174             if ( y 
> endScanBottomY 
) 
2181     visPropArray
.push_back(NULL
); 
2183     wxPGProperty
* nextP 
= visPropArray
[0]; 
2185     int gridWidth 
= state
->m_width
; 
2188     for ( unsigned int arrInd
=1; 
2189           nextP 
&& y 
<= lastItemBottomY
; 
2192         wxPGProperty
* p 
= nextP
; 
2193         nextP 
= visPropArray
[arrInd
]; 
2195         int rowHeight 
= m_fontHeight
+(m_spacingy
*2)+1; 
2196         int textMarginHere 
= x
; 
2197         int renderFlags 
= 0; 
2199         int greyDepth 
= m_marginWidth
; 
2200         if ( !(windowStyle 
& wxPG_HIDE_CATEGORIES
) ) 
2201             greyDepth 
= (((int)p
->m_depthBgCol
)-1) * m_subgroup_extramargin 
+ m_marginWidth
; 
2203         int greyDepthX 
= greyDepth 
- xRelMod
; 
2205         // Use basic depth if in non-categoric mode and parent is base array. 
2206         if ( !(windowStyle 
& wxPG_HIDE_CATEGORIES
) || p
->GetParent() != m_pState
->m_properties 
) 
2208             textMarginHere 
+= ((unsigned int)((p
->m_depth
-1)*m_subgroup_extramargin
)); 
2211         // Paint margin area 
2212         dc
.SetBrush(marginBrush
); 
2213         dc
.SetPen(marginPen
); 
2214         dc
.DrawRectangle( -xRelMod
, y
, greyDepth
, lh 
); 
2216         dc
.SetPen( linepen 
); 
2222         // Modified by JACS to not draw a margin if wxPG_HIDE_MARGIN is specified, since it 
2223         // looks better, at least under Windows when we have a themed border (the themed-window-specific 
2224         // whitespace between the real border and the propgrid margin exacerbates the double-border look). 
2226         // Is this or its parent themed? 
2227         bool suppressMarginEdge 
= (GetWindowStyle() & wxPG_HIDE_MARGIN
) && 
2228             (((GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_THEME
) || 
2229             (((GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_NONE
) && ((GetParent()->GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_THEME
))); 
2231         bool suppressMarginEdge 
= false; 
2233         if (!suppressMarginEdge
) 
2234             dc
.DrawLine( greyDepthX
, y
, greyDepthX
, y2 
); 
2237             // Blank out the margin edge 
2238             dc
.SetPen(wxPen(GetBackgroundColour())); 
2239             dc
.DrawLine( greyDepthX
, y
, greyDepthX
, y2 
); 
2240             dc
.SetPen( linepen 
); 
2247         for ( si
=0; si
<colWidths
.size(); si
++ ) 
2249             sx 
+= colWidths
[si
]; 
2250             dc
.DrawLine( sx
, y
, sx
, y2 
); 
2253         // Horizontal Line, below 
2254         //   (not if both this and next is category caption) 
2255         if ( p
->IsCategory() && 
2256              nextP 
&& nextP
->IsCategory() ) 
2257             dc
.SetPen(m_colCapBack
); 
2259         dc
.DrawLine( greyDepthX
, y2
-1, gridWidth
-xRelMod
, y2
-1 ); 
2262         // Need to override row colours? 
2266         bool isSelected 
= state
->DoIsPropertySelected(p
); 
2270             // Disabled may get different colour. 
2271             if ( !p
->IsEnabled() ) 
2273                 renderFlags 
|= wxPGCellRenderer::Disabled 
| 
2274                                wxPGCellRenderer::DontUseCellFgCol
; 
2275                 rowFgCol 
= m_colDisPropFore
; 
2280 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2281             if ( p 
== firstSelected 
) 
2282                 wasSelectedPainted 
= true; 
2285             renderFlags 
|= wxPGCellRenderer::Selected
; 
2287             if ( !p
->IsCategory() ) 
2289                 renderFlags 
|= wxPGCellRenderer::DontUseCellFgCol 
| 
2290                                wxPGCellRenderer::DontUseCellBgCol
; 
2292                 if ( reallyFocused 
&& p 
== firstSelected 
) 
2294                     rowFgCol 
= m_colSelFore
; 
2295                     rowBgCol 
= selBackCol
; 
2297                 else if ( isPgEnabled 
) 
2299                     rowFgCol 
= m_colPropFore
; 
2300                     if ( p 
== firstSelected 
) 
2301                         rowBgCol 
= m_colMargin
; 
2303                         rowBgCol 
= selBackCol
; 
2307                     rowFgCol 
= m_colDisPropFore
; 
2308                     rowBgCol 
= selBackCol
; 
2315         if ( rowBgCol
.IsOk() ) 
2316             rowBgBrush 
= wxBrush(rowBgCol
); 
2318         if ( HasInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
) ) 
2319             renderFlags 
= renderFlags 
& ~wxPGCellRenderer::DontUseCellColours
; 
2322         // Fill additional margin area with background colour of first cell 
2323         if ( greyDepthX 
< textMarginHere 
) 
2325             if ( !(renderFlags 
& wxPGCellRenderer::DontUseCellBgCol
) ) 
2327                 wxPGCell
& cell 
= p
->GetCell(0); 
2328                 rowBgCol 
= cell
.GetBgCol(); 
2329                 rowBgBrush 
= wxBrush(rowBgCol
); 
2331             dc
.SetBrush(rowBgBrush
); 
2332             dc
.SetPen(rowBgCol
); 
2333             dc
.DrawRectangle(greyDepthX
+1, y
, 
2334                              textMarginHere
-greyDepthX
, lh
-1); 
2337         bool fontChanged 
= false; 
2339         // Expander button rectangle 
2340         wxRect 
butRect( ((p
->m_depth 
- 1) * m_subgroup_extramargin
) - xRelMod
, 
2345         // Default cell rect fill the entire row 
2346         wxRect 
cellRect(greyDepthX
, y
, 
2347                         gridWidth 
- greyDepth 
+ 2, rowHeight
-1 ); 
2349         bool isCategory 
= p
->IsCategory(); 
2353             dc
.SetFont(m_captionFont
); 
2356             if ( renderFlags 
& wxPGCellRenderer::DontUseCellBgCol 
) 
2358                 dc
.SetBrush(rowBgBrush
); 
2359                 dc
.SetPen(rowBgCol
); 
2362             if ( renderFlags 
& wxPGCellRenderer::DontUseCellFgCol 
) 
2364                 dc
.SetTextForeground(rowFgCol
); 
2369             // Fine tune button rectangle to actually fit the cell 
2370             if ( butRect
.x 
> 0 ) 
2371                 butRect
.x 
+= IN_CELL_EXPANDER_BUTTON_X_ADJUST
; 
2373             if ( p
->m_flags 
& wxPG_PROP_MODIFIED 
&& 
2374                  (windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
2376                 dc
.SetFont(m_captionFont
); 
2380             // Magic fine-tuning for non-category rows 
2384         int firstCellWidth 
= colWidths
[0] - (greyDepthX 
- m_marginWidth
); 
2385         int firstCellX 
= cellRect
.x
; 
2387         // Calculate cellRect.x for the last cell 
2388         unsigned int ci 
= 0; 
2390         for ( ci
=0; ci
<colWidths
.size(); ci
++ ) 
2391             cellX 
+= colWidths
[ci
]; 
2394         // Draw cells from back to front so that we can easily tell if the 
2395         // cell on the right was empty from text 
2396         bool prevFilled 
= true; 
2397         ci 
= colWidths
.size(); 
2406                 textXAdd 
= textMarginHere 
- greyDepthX
; 
2407                 cellRect
.width 
= firstCellWidth
; 
2408                 cellRect
.x 
= firstCellX
; 
2412                 int colWidth 
= colWidths
[ci
]; 
2413                 cellRect
.width 
= colWidth
; 
2414                 cellRect
.x 
-= colWidth
; 
2417             // Merge with column to the right? 
2418             if ( !prevFilled 
&& isCategory 
) 
2420                 cellRect
.width 
+= colWidths
[ci
+1]; 
2424                 cellRect
.width 
-= 1; 
2426             wxWindow
* cellEditor 
= NULL
; 
2427             int cellRenderFlags 
= renderFlags
; 
2429             // Tree Item Button (must be drawn before clipping is set up) 
2430             if ( ci 
== 0 && !HasFlag(wxPG_HIDE_MARGIN
) && p
->HasVisibleChildren() ) 
2431                 DrawExpanderButton( dc
, butRect
, p 
); 
2434             if ( isSelected 
&& (ci 
== 1 || ci 
== m_selColumn
) ) 
2436                 if ( p 
== firstSelected 
) 
2438                     if ( ci 
== 1 && m_wndEditor 
) 
2439                         cellEditor 
= m_wndEditor
; 
2440                     else if ( ci 
== m_selColumn 
&& m_labelEditor 
) 
2441                         cellEditor 
= m_labelEditor
; 
2446                     wxColour editorBgCol 
= 
2447                         cellEditor
->GetBackgroundColour(); 
2448                     dc
.SetBrush(editorBgCol
); 
2449                     dc
.SetPen(editorBgCol
); 
2450                     dc
.SetTextForeground(m_colPropFore
); 
2451                     dc
.DrawRectangle(cellRect
); 
2453                     if ( m_dragStatus 
!= 0 || 
2454                          (m_iFlags 
& wxPG_FL_CUR_USES_CUSTOM_IMAGE
) ) 
2459                     dc
.SetBrush(m_colPropBack
); 
2460                     dc
.SetPen(m_colPropBack
); 
2461                     if ( p
->IsEnabled() ) 
2462                         dc
.SetTextForeground(m_colPropFore
); 
2464                         dc
.SetTextForeground(m_colDisPropFore
); 
2469                 if ( renderFlags 
& wxPGCellRenderer::DontUseCellBgCol 
) 
2471                     dc
.SetBrush(rowBgBrush
); 
2472                     dc
.SetPen(rowBgCol
); 
2475                 if ( renderFlags 
& wxPGCellRenderer::DontUseCellFgCol 
) 
2477                     dc
.SetTextForeground(rowFgCol
); 
2481             dc
.SetClippingRegion(cellRect
); 
2483             cellRect
.x 
+= textXAdd
; 
2484             cellRect
.width 
-= textXAdd
; 
2489                 wxPGCellRenderer
* renderer
; 
2490                 int cmnVal 
= p
->GetCommonValue(); 
2491                 if ( cmnVal 
== -1 || ci 
!= 1 ) 
2493                     renderer 
= p
->GetCellRenderer(ci
); 
2494                     prevFilled 
= renderer
->Render(dc
, cellRect
, this, 
2500                     renderer 
= GetCommonValue(cmnVal
)->GetRenderer(); 
2501                     prevFilled 
= renderer
->Render(dc
, cellRect
, this, 
2511             dc
.DestroyClippingRegion(); // Is this really necessary? 
2516             dc
.SetFont(normalFont
); 
2521     // Refresh editor controls (seems not needed on msw) 
2522     // NOTE: This code is mandatory for GTK! 
2523 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2524     if ( wasSelectedPainted 
) 
2527             m_wndEditor
->Refresh(); 
2529             m_wndEditor2
->Refresh(); 
2536 // ----------------------------------------------------------------------- 
2538 wxRect 
wxPropertyGrid::GetPropertyRect( const wxPGProperty
* p1
, const wxPGProperty
* p2 
) const 
2542     if ( m_width 
< 10 || m_height 
< 10 || 
2543          !m_pState
->m_properties
->GetChildCount() || 
2545         return wxRect(0,0,0,0); 
2550     // Return rect which encloses the given property range 
2551     // (in logical grid coordinates) 
2554     int visTop 
= p1
->GetY(); 
2557         visBottom 
= p2
->GetY() + m_lineHeight
; 
2559         visBottom 
= m_height 
+ visTop
; 
2561     // If seleced property is inside the range, we'll extend the range to include 
2563     wxPGProperty
* selected 
= GetSelection(); 
2566         int selectedY 
= selected
->GetY(); 
2567         if ( selectedY 
>= visTop 
&& selectedY 
< visBottom 
) 
2569             wxWindow
* editor 
= GetEditorControl(); 
2572                 int visBottom2 
= selectedY 
+ editor
->GetSize().y
; 
2573                 if ( visBottom2 
> visBottom 
) 
2574                     visBottom 
= visBottom2
; 
2579     return wxRect(0,visTop
-vy
,m_pState
->m_width
,visBottom
-visTop
); 
2582 // ----------------------------------------------------------------------- 
2584 void wxPropertyGrid::DrawItems( const wxPGProperty
* p1
, const wxPGProperty
* p2 
) 
2589     if ( m_pState
->m_itemsAdded 
) 
2590         PrepareAfterItemsAdded(); 
2592     wxRect r 
= GetPropertyRect(p1
, p2
); 
2595         // Convert rectangle from logical grid coordinates to physical ones 
2597         GetViewStart(&vx
, &vy
); 
2598         vx 
*= wxPG_PIXELS_PER_UNIT
; 
2599         vy 
*= wxPG_PIXELS_PER_UNIT
; 
2606 // ----------------------------------------------------------------------- 
2608 void wxPropertyGrid::RefreshProperty( wxPGProperty
* p 
) 
2610     if ( m_pState
->DoIsPropertySelected(p
) ) 
2612         // NB: We must copy the selection. 
2613         wxArrayPGProperty selection 
= m_pState
->m_selection
; 
2614         DoSetSelection(selection
, wxPG_SEL_FORCE
); 
2617     DrawItemAndChildren(p
); 
2620 // ----------------------------------------------------------------------- 
2622 void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty
* p 
) 
2627     // Draw item, children, and parent too, if it is not category 
2628     wxPGProperty
* parent 
= p
->GetParent(); 
2631             !parent
->IsCategory() && 
2632             parent
->GetParent() ) 
2635          parent 
= parent
->GetParent(); 
2638     DrawItemAndChildren(p
); 
2641 void wxPropertyGrid::DrawItemAndChildren( wxPGProperty
* p 
) 
2643     wxCHECK_RET( p
, wxT("invalid property id") ); 
2645     // Do not draw if in non-visible page 
2646     if ( p
->GetParentState() != m_pState 
) 
2649     // do not draw a single item if multiple pending 
2650     if ( m_pState
->m_itemsAdded 
|| m_frozen 
) 
2653     // Update child control. 
2654     wxPGProperty
* selected 
= GetSelection(); 
2655     if ( selected 
&& selected
->GetParent() == p 
) 
2658     const wxPGProperty
* lastDrawn 
= p
->GetLastVisibleSubItem(); 
2660     DrawItems(p
, lastDrawn
); 
2663 // ----------------------------------------------------------------------- 
2665 void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground
), 
2666                               const wxRect 
*rect 
) 
2668     PrepareAfterItemsAdded(); 
2670     wxWindow::Refresh(false, rect
); 
2672 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2673     // I think this really helps only GTK+1.2 
2674     if ( m_wndEditor 
) m_wndEditor
->Refresh(); 
2675     if ( m_wndEditor2 
) m_wndEditor2
->Refresh(); 
2679 // ----------------------------------------------------------------------- 
2680 // wxPropertyGrid global operations 
2681 // ----------------------------------------------------------------------- 
2683 void wxPropertyGrid::Clear() 
2685     m_pState
->DoClear(); 
2691     RecalculateVirtualSize(); 
2693     // Need to clear some area at the end 
2695         RefreshRect(wxRect(0, 0, m_width
, m_height
)); 
2698 // ----------------------------------------------------------------------- 
2700 bool wxPropertyGrid::EnableCategories( bool enable 
) 
2707         // Enable categories 
2710         m_windowStyle 
&= ~(wxPG_HIDE_CATEGORIES
); 
2715         // Disable categories 
2717         m_windowStyle 
|= wxPG_HIDE_CATEGORIES
; 
2720     if ( !m_pState
->EnableCategories(enable
) ) 
2725         if ( m_windowStyle 
& wxPG_AUTO_SORT 
) 
2727             m_pState
->m_itemsAdded 
= 1; // force 
2728             PrepareAfterItemsAdded(); 
2732         m_pState
->m_itemsAdded 
= 1; 
2734     // No need for RecalculateVirtualSize() here - it is already called in 
2735     // wxPropertyGridPageState method above. 
2742 // ----------------------------------------------------------------------- 
2744 void wxPropertyGrid::SwitchState( wxPropertyGridPageState
* pNewState 
) 
2746     wxASSERT( pNewState 
); 
2747     wxASSERT( pNewState
->GetGrid() ); 
2749     if ( pNewState 
== m_pState 
) 
2752     wxArrayPGProperty oldSelection 
= m_pState
->m_selection
; 
2754     // Call ClearSelection() instead of DoClearSelection() 
2755     // so that selection clear events are not sent. 
2758     m_pState
->m_selection 
= oldSelection
; 
2760     bool orig_mode 
= m_pState
->IsInNonCatMode(); 
2761     bool new_state_mode 
= pNewState
->IsInNonCatMode(); 
2763     m_pState 
= pNewState
; 
2766     int pgWidth 
= GetClientSize().x
; 
2767     if ( HasVirtualWidth() ) 
2769         int minWidth 
= pgWidth
; 
2770         if ( pNewState
->m_width 
< minWidth 
) 
2772             pNewState
->m_width 
= minWidth
; 
2773             pNewState
->CheckColumnWidths(); 
2779         // Just in case, fully re-center splitter 
2780         //if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) ) 
2781         //    pNewState->m_fSplitterX = -1.0; 
2783         pNewState
->OnClientWidthChange(pgWidth
, 
2784                                        pgWidth 
- pNewState
->m_width
); 
2789     // If necessary, convert state to correct mode. 
2790     if ( orig_mode 
!= new_state_mode 
) 
2792         // This should refresh as well. 
2793         EnableCategories( orig_mode
?false:true ); 
2795     else if ( !m_frozen 
) 
2797         // Refresh, if not frozen. 
2798         m_pState
->PrepareAfterItemsAdded(); 
2800         // Reselect (Use SetSelection() instead of Do-variant so that 
2801         // events won't be sent). 
2802         SetSelection(m_pState
->m_selection
); 
2804         RecalculateVirtualSize(0); 
2808         m_pState
->m_itemsAdded 
= 1; 
2811 // ----------------------------------------------------------------------- 
2813 // Call to SetSplitterPosition will always disable splitter auto-centering 
2814 // if parent window is shown. 
2815 void wxPropertyGrid::DoSetSplitterPosition( int newxpos
, 
2819     if ( ( newxpos 
< wxPG_DRAG_MARGIN 
) ) 
2822     wxPropertyGridPageState
* state 
= m_pState
; 
2824     if ( flags 
& wxPG_SPLITTER_FROM_EVENT 
) 
2825         state
->m_dontCenterSplitter 
= true; 
2827     state
->DoSetSplitterPosition(newxpos
, splitterIndex
, flags
); 
2829     if ( flags 
& wxPG_SPLITTER_REFRESH 
) 
2831         if ( GetSelection() ) 
2832             CorrectEditorWidgetSizeX(); 
2840 // ----------------------------------------------------------------------- 
2842 void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing 
) 
2844     wxPropertyGridPageState
* state 
= m_pState
; 
2846         state
->ResetColumnSizes(0); 
2848     if ( enableAutoResizing 
&& HasFlag(wxPG_SPLITTER_AUTO_CENTER
) ) 
2849         m_pState
->m_dontCenterSplitter 
= false; 
2852 // ----------------------------------------------------------------------- 
2854 void wxPropertyGrid::CenterSplitter( bool enableAutoResizing 
) 
2856     SetSplitterPosition( m_width
/2 ); 
2857     if ( enableAutoResizing 
&& HasFlag(wxPG_SPLITTER_AUTO_CENTER
) ) 
2858         m_pState
->m_dontCenterSplitter 
= false; 
2861 // ----------------------------------------------------------------------- 
2862 // wxPropertyGrid item iteration (GetNextProperty etc.) methods 
2863 // ----------------------------------------------------------------------- 
2865 // Returns nearest paint visible property (such that will be painted unless 
2866 // window is scrolled or resized). If given property is paint visible, then 
2867 // it itself will be returned 
2868 wxPGProperty
* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty
* p 
) const 
2870     int vx
,vy1
;// Top left corner of client 
2871     GetViewStart(&vx
,&vy1
); 
2872     vy1 
*= wxPG_PIXELS_PER_UNIT
; 
2874     int vy2 
= vy1 
+ m_height
; 
2875     int propY 
= p
->GetY2(m_lineHeight
); 
2877     if ( (propY 
+ m_lineHeight
) < vy1 
) 
2880         return DoGetItemAtY( vy1 
); 
2882     else if ( propY 
> vy2 
) 
2885         return DoGetItemAtY( vy2 
); 
2888     // Itself paint visible 
2893 // ----------------------------------------------------------------------- 
2894 // Methods related to change in value, value modification and sending events 
2895 // ----------------------------------------------------------------------- 
2897 // commits any changes in editor of selected property 
2898 // return true if validation did not fail 
2899 // flags are same as with DoSelectProperty 
2900 bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags 
) 
2902     // Committing already? 
2903     if ( m_inCommitChangesFromEditor 
) 
2906     // Don't do this if already processing editor event. It might 
2907     // induce recursive dialogs and crap like that. 
2908     if ( m_iFlags 
& wxPG_FL_IN_HANDLECUSTOMEDITOREVENT 
) 
2910         if ( m_inDoPropertyChanged 
) 
2916     wxPGProperty
* selected 
= GetSelection(); 
2919          IsEditorsValueModified() && 
2920          (m_iFlags 
& wxPG_FL_INITIALIZED
) && 
2923         m_inCommitChangesFromEditor 
= true; 
2925         wxVariant 
variant(selected
->GetValueRef()); 
2926         bool valueIsPending 
= false; 
2928         // JACS - necessary to avoid new focus being found spuriously within OnIdle 
2929         // due to another window getting focus 
2930         wxWindow
* oldFocus 
= m_curFocused
; 
2932         bool validationFailure 
= false; 
2933         bool forceSuccess 
= (flags 
& (wxPG_SEL_NOVALIDATE
|wxPG_SEL_FORCE
)) ? true : false; 
2935         m_chgInfo_changedProperty 
= NULL
; 
2937         // If truly modified, schedule value as pending. 
2938         if ( selected
->GetEditorClass()-> 
2939                 GetValueFromControl( variant
, 
2941                                      GetEditorControl() ) ) 
2943             if ( DoEditorValidate() && 
2944                  PerformValidation(selected
, variant
) ) 
2946                 valueIsPending 
= true; 
2950                 validationFailure 
= true; 
2955             EditorsValueWasNotModified(); 
2958         m_inCommitChangesFromEditor 
= false; 
2962         if ( validationFailure 
&& !forceSuccess 
) 
2966                 oldFocus
->SetFocus(); 
2967                 m_curFocused 
= oldFocus
; 
2970             res 
= OnValidationFailure(selected
, variant
); 
2972             // Now prevent further validation failure messages 
2975                 EditorsValueWasNotModified(); 
2976                 OnValidationFailureReset(selected
); 
2979         else if ( valueIsPending 
) 
2981             DoPropertyChanged( selected
, flags 
); 
2982             EditorsValueWasNotModified(); 
2991 // ----------------------------------------------------------------------- 
2993 bool wxPropertyGrid::PerformValidation( wxPGProperty
* p
, wxVariant
& pendingValue
, 
2997     // Runs all validation functionality. 
2998     // Returns true if value passes all tests. 
3001     m_validationInfo
.m_failureBehavior 
= m_permanentValidationFailureBehavior
; 
3002     m_validationInfo
.m_isFailing 
= true; 
3005     // Variant list a special value that cannot be validated 
3007     if ( pendingValue
.GetType() != wxPG_VARIANT_TYPE_LIST 
) 
3009         if ( !p
->ValidateValue(pendingValue
, m_validationInfo
) ) 
3014     // Adapt list to child values, if necessary 
3015     wxVariant listValue 
= pendingValue
; 
3016     wxVariant
* pPendingValue 
= &pendingValue
; 
3017     wxVariant
* pList 
= NULL
; 
3019     // If parent has wxPG_PROP_AGGREGATE flag, or uses composite 
3020     // string value, then we need treat as it was changed instead 
3021     // (or, in addition, as is the case with composite string parent). 
3022     // This includes creating list variant for child values. 
3024     wxPGProperty
* pwc 
= p
->GetParent(); 
3025     wxPGProperty
* changedProperty 
= p
; 
3026     wxPGProperty
* baseChangedProperty 
= changedProperty
; 
3027     wxVariant bcpPendingList
; 
3029     listValue 
= pendingValue
; 
3030     listValue
.SetName(p
->GetBaseName()); 
3033             (pwc
->HasFlag(wxPG_PROP_AGGREGATE
) || pwc
->HasFlag(wxPG_PROP_COMPOSED_VALUE
)) ) 
3035         wxVariantList tempList
; 
3036         wxVariant 
lv(tempList
, pwc
->GetBaseName()); 
3037         lv
.Append(listValue
); 
3039         pPendingValue 
= &listValue
; 
3041         if ( pwc
->HasFlag(wxPG_PROP_AGGREGATE
) ) 
3043             baseChangedProperty 
= pwc
; 
3044             bcpPendingList 
= lv
; 
3047         changedProperty 
= pwc
; 
3048         pwc 
= pwc
->GetParent(); 
3052     wxPGProperty
* evtChangingProperty 
= changedProperty
; 
3054     if ( pPendingValue
->GetType() != wxPG_VARIANT_TYPE_LIST 
) 
3056         value 
= *pPendingValue
; 
3060         // Convert list to child values 
3061         pList 
= pPendingValue
; 
3062         changedProperty
->AdaptListToValue( *pPendingValue
, &value 
); 
3065     wxVariant evtChangingValue 
= value
; 
3067     if ( flags 
& SendEvtChanging 
) 
3069         // FIXME: After proper ValueToString()s added, remove 
3070         // this. It is just a temporary fix, as evt_changing 
3071         // will simply not work for wxPG_PROP_COMPOSED_VALUE 
3072         // (unless it is selected, and textctrl editor is open). 
3073         if ( changedProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3075             evtChangingProperty 
= baseChangedProperty
; 
3076             if ( evtChangingProperty 
!= p 
) 
3078                 evtChangingProperty
->AdaptListToValue( bcpPendingList
, &evtChangingValue 
); 
3082                 evtChangingValue 
= pendingValue
; 
3086         if ( evtChangingProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3088             if ( changedProperty 
== GetSelection() ) 
3090                 wxWindow
* editor 
= GetEditorControl(); 
3091                 wxASSERT( editor
->IsKindOf(CLASSINFO(wxTextCtrl
)) ); 
3092                 evtChangingValue 
= wxStaticCast(editor
, wxTextCtrl
)->GetValue(); 
3096                 wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value.")); 
3101     wxASSERT( m_chgInfo_changedProperty 
== NULL 
); 
3102     m_chgInfo_changedProperty 
= changedProperty
; 
3103     m_chgInfo_baseChangedProperty 
= baseChangedProperty
; 
3104     m_chgInfo_pendingValue 
= value
; 
3107         m_chgInfo_valueList 
= *pList
; 
3109         m_chgInfo_valueList
.MakeNull(); 
3111     // If changedProperty is not property which value was edited, 
3112     // then call wxPGProperty::ValidateValue() for that as well. 
3113     if ( p 
!= changedProperty 
&& value
.GetType() != wxPG_VARIANT_TYPE_LIST 
) 
3115         if ( !changedProperty
->ValidateValue(value
, m_validationInfo
) ) 
3119     if ( flags 
& SendEvtChanging 
) 
3121         // SendEvent returns true if event was vetoed 
3122         if ( SendEvent( wxEVT_PG_CHANGING
, evtChangingProperty
, 
3123                         &evtChangingValue 
) ) 
3127     if ( flags 
& IsStandaloneValidation 
) 
3129         // If called in 'generic' context, we need to reset 
3130         // m_chgInfo_changedProperty and write back translated value. 
3131         m_chgInfo_changedProperty 
= NULL
; 
3132         pendingValue 
= value
; 
3135     m_validationInfo
.m_isFailing 
= false; 
3140 // ----------------------------------------------------------------------- 
3143 wxStatusBar
* wxPropertyGrid::GetStatusBar() 
3145     wxWindow
* topWnd 
= ::wxGetTopLevelParent(this); 
3146     if ( topWnd 
&& topWnd
->IsKindOf(CLASSINFO(wxFrame
)) ) 
3148         wxFrame
* pFrame 
= wxStaticCast(topWnd
, wxFrame
); 
3150             return pFrame
->GetStatusBar(); 
3156 // ----------------------------------------------------------------------- 
3158 void wxPropertyGrid::DoShowPropertyError( wxPGProperty
* WXUNUSED(property
), const wxString
& msg 
) 
3160     if ( !msg
.length() ) 
3164     if ( !wxPGGlobalVars
->m_offline 
) 
3166         wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3169             pStatusBar
->SetStatusText(msg
); 
3175     ::wxMessageBox(msg
, _("Property Error")); 
3178 // ----------------------------------------------------------------------- 
3180 void wxPropertyGrid::DoHidePropertyError( wxPGProperty
* WXUNUSED(property
) ) 
3183     if ( !wxPGGlobalVars
->m_offline 
) 
3185         wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3188             pStatusBar
->SetStatusText(wxEmptyString
); 
3195 // ----------------------------------------------------------------------- 
3197 bool wxPropertyGrid::OnValidationFailure( wxPGProperty
* property
, 
3198                                           wxVariant
& invalidValue 
) 
3200     if ( m_inOnValidationFailure 
) 
3203     m_inOnValidationFailure 
= true; 
3204     wxON_BLOCK_EXIT_SET(m_inOnValidationFailure
, false); 
3206     wxWindow
* editor 
= GetEditorControl(); 
3207     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3209     if ( m_inDoSelectProperty 
) 
3211         // When property selection is being changed, do not display any 
3212         // messages, if some were already shown for this property. 
3213         if ( property
->HasFlag(wxPG_PROP_INVALID_VALUE
) ) 
3215             m_validationInfo
.m_failureBehavior 
= 
3216                 vfb 
& ~(wxPG_VFB_SHOW_MESSAGE 
| 
3217                         wxPG_VFB_SHOW_MESSAGEBOX 
| 
3218                         wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR
); 
3222     // First call property's handler 
3223     property
->OnValidationFailure(invalidValue
); 
3225     bool res 
= DoOnValidationFailure(property
, invalidValue
); 
3228     // For non-wxTextCtrl editors, we do need to revert the value 
3229     if ( !editor
->IsKindOf(CLASSINFO(wxTextCtrl
)) && 
3230          property 
== GetSelection() ) 
3232         property
->GetEditorClass()->UpdateControl(property
, editor
); 
3235     property
->SetFlag(wxPG_PROP_INVALID_VALUE
); 
3240 bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty
* property
, wxVariant
& WXUNUSED(invalidValue
) ) 
3242     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3244     if ( vfb 
& wxPG_VFB_BEEP 
) 
3247     if ( (vfb 
& wxPG_VFB_MARK_CELL
) && 
3248          !property
->HasFlag(wxPG_PROP_INVALID_VALUE
) ) 
3250         unsigned int colCount 
= m_pState
->GetColumnCount(); 
3252         // We need backup marked property's cells 
3253         m_propCellsBackup 
= property
->m_cells
; 
3255         wxColour vfbFg 
= *wxWHITE
; 
3256         wxColour vfbBg 
= *wxRED
; 
3258         property
->EnsureCells(colCount
); 
3260         for ( unsigned int i
=0; i
<colCount
; i
++ ) 
3262             wxPGCell
& cell 
= property
->m_cells
[i
]; 
3263             cell
.SetFgCol(vfbFg
); 
3264             cell
.SetBgCol(vfbBg
); 
3267         DrawItemAndChildren(property
); 
3269         if ( property 
== GetSelection() ) 
3271             SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
); 
3273             wxWindow
* editor 
= GetEditorControl(); 
3276                 editor
->SetForegroundColour(vfbFg
); 
3277                 editor
->SetBackgroundColour(vfbBg
); 
3282     if ( vfb 
& (wxPG_VFB_SHOW_MESSAGE 
| 
3283                 wxPG_VFB_SHOW_MESSAGEBOX 
| 
3284                 wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR
) ) 
3286         wxString msg 
= m_validationInfo
.m_failureMessage
; 
3288         if ( !msg
.length() ) 
3289             msg 
= _("You have entered invalid value. Press ESC to cancel editing."); 
3292         if ( vfb 
& wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR 
) 
3294             if ( !wxPGGlobalVars
->m_offline 
) 
3296                 wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3298                     pStatusBar
->SetStatusText(msg
); 
3303         if ( vfb 
& wxPG_VFB_SHOW_MESSAGE 
) 
3304             DoShowPropertyError(property
, msg
); 
3306         if ( vfb 
& wxPG_VFB_SHOW_MESSAGEBOX 
) 
3307             ::wxMessageBox(msg
, _("Property Error")); 
3310     return (vfb 
& wxPG_VFB_STAY_IN_PROPERTY
) ? false : true; 
3313 // ----------------------------------------------------------------------- 
3315 void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty
* property 
) 
3317     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3319     if ( vfb 
& wxPG_VFB_MARK_CELL 
) 
3322         property
->m_cells 
= m_propCellsBackup
; 
3324         ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
); 
3326         if ( property 
== GetSelection() && GetEditorControl() ) 
3328             // Calling this will recreate the control, thus resetting its colour 
3329             RefreshProperty(property
); 
3333             DrawItemAndChildren(property
); 
3338     if ( vfb 
& wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR 
) 
3340         if ( !wxPGGlobalVars
->m_offline 
) 
3342             wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3344                 pStatusBar
->SetStatusText(wxEmptyString
); 
3349     if ( vfb 
& wxPG_VFB_SHOW_MESSAGE 
) 
3351         DoHidePropertyError(property
); 
3354     m_validationInfo
.m_isFailing 
= false; 
3357 // ----------------------------------------------------------------------- 
3359 // flags are same as with DoSelectProperty 
3360 bool wxPropertyGrid::DoPropertyChanged( wxPGProperty
* p
, unsigned int selFlags 
) 
3362     if ( m_inDoPropertyChanged 
) 
3365     m_inDoPropertyChanged 
= true; 
3366     wxON_BLOCK_EXIT_SET(m_inDoPropertyChanged
, false); 
3368     wxPGProperty
* selected 
= GetSelection(); 
3370     m_pState
->m_anyModified 
= 1; 
3372     // If property's value is being changed, assume it is valid 
3373     OnValidationFailureReset(selected
); 
3375     // Maybe need to update control 
3376     wxASSERT( m_chgInfo_changedProperty 
!= NULL 
); 
3378     // These values were calculated in PerformValidation() 
3379     wxPGProperty
* changedProperty 
= m_chgInfo_changedProperty
; 
3380     wxVariant value 
= m_chgInfo_pendingValue
; 
3382     wxPGProperty
* topPaintedProperty 
= changedProperty
; 
3384     while ( !topPaintedProperty
->IsCategory() && 
3385             !topPaintedProperty
->IsRoot() ) 
3387         topPaintedProperty 
= topPaintedProperty
->GetParent(); 
3390     changedProperty
->SetValue(value
, &m_chgInfo_valueList
, wxPG_SETVAL_BY_USER
); 
3392     // NB: Call GetEditorControl() as late as possible, because OnSetValue() 
3393     //     and perhaps other user-defined virtual functions may change it. 
3394     wxWindow
* editor 
= GetEditorControl(); 
3396     // Set as Modified (not if dragging just began) 
3397     if ( !(p
->m_flags 
& wxPG_PROP_MODIFIED
) ) 
3399         p
->m_flags 
|= wxPG_PROP_MODIFIED
; 
3400         if ( p 
== selected 
&& (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3403                 SetCurControlBoldFont(); 
3409     // Propagate updates to parent(s) 
3411     wxPGProperty
* prevPwc 
= NULL
; 
3413     while ( prevPwc 
!= topPaintedProperty 
) 
3415         pwc
->m_flags 
|= wxPG_PROP_MODIFIED
; 
3417         if ( pwc 
== selected 
&& (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3420                 SetCurControlBoldFont(); 
3424         pwc 
= pwc
->GetParent(); 
3427     // Draw the actual property 
3428     DrawItemAndChildren( topPaintedProperty 
); 
3431     // If value was set by wxPGProperty::OnEvent, then update the editor 
3433     if ( selFlags 
& wxPG_SEL_DIALOGVAL 
) 
3439 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
3440         if ( m_wndEditor 
) m_wndEditor
->Refresh(); 
3441         if ( m_wndEditor2 
) m_wndEditor2
->Refresh(); 
3446     wxASSERT( !changedProperty
->GetParent()->HasFlag(wxPG_PROP_AGGREGATE
) ); 
3448     // If top parent has composite string value, then send to child parents, 
3449     // starting from baseChangedProperty. 
3450     if ( changedProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3452         pwc 
= m_chgInfo_baseChangedProperty
; 
3454         while ( pwc 
!= changedProperty 
) 
3456             SendEvent( wxEVT_PG_CHANGED
, pwc
, NULL 
); 
3457             pwc 
= pwc
->GetParent(); 
3461     SendEvent( wxEVT_PG_CHANGED
, changedProperty
, NULL 
); 
3466 // ----------------------------------------------------------------------- 
3468 bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id
, wxVariant newValue 
) 
3470     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
3472     m_chgInfo_changedProperty 
= NULL
; 
3474     if ( PerformValidation(p
, newValue
) ) 
3476         DoPropertyChanged(p
); 
3481         OnValidationFailure(p
, newValue
); 
3487 // ----------------------------------------------------------------------- 
3489 wxVariant 
wxPropertyGrid::GetUncommittedPropertyValue() 
3491     wxPGProperty
* prop 
= GetSelectedProperty(); 
3494         return wxNullVariant
; 
3496     wxTextCtrl
* tc 
= GetEditorTextCtrl(); 
3497     wxVariant value 
= prop
->GetValue(); 
3499     if ( !tc 
|| !IsEditorsValueModified() ) 
3502     if ( !prop
->StringToValue(value
, tc
->GetValue()) ) 
3505     if ( !PerformValidation(prop
, value
, IsStandaloneValidation
) ) 
3506         return prop
->GetValue(); 
3511 // ----------------------------------------------------------------------- 
3513 // Runs wxValidator for the selected property 
3514 bool wxPropertyGrid::DoEditorValidate() 
3516 #if wxUSE_VALIDATORS 
3517     wxRecursionGuard 
guard(m_validatingEditor
); 
3518     if ( guard
.IsInside() ) 
3521     wxPGProperty
* selected 
= GetSelection(); 
3524         wxWindow
* wnd 
= GetEditorControl(); 
3526         wxValidator
* validator 
= selected
->GetValidator(); 
3527         if ( validator 
&& wnd 
) 
3529             validator
->SetWindow(wnd
); 
3530             if ( !validator
->Validate(this) ) 
3538 // ----------------------------------------------------------------------- 
3540 void wxPropertyGrid::HandleCustomEditorEvent( wxEvent 
&event 
) 
3542     // It is possible that this handler receives event even before 
3543     // the control has been properly initialized. Let's skip the 
3544     // event handling in that case. 
3548     // Don't care about the event if it originated from the 
3549     // 'label editor'. In this function we only care about the 
3550     // property value editor. 
3551     if ( m_labelEditor 
&& event
.GetId() == m_labelEditor
->GetId() ) 
3557     wxPGProperty
* selected 
= GetSelection(); 
3559     // Somehow, event is handled after property has been deselected. 
3560     // Possibly, but very rare. 
3562           selected
->HasFlag(wxPG_PROP_BEING_DELETED
) || 
3563           m_inOnValidationFailure 
|| 
3564           // Also don't handle editor event if wxEVT_PG_CHANGED or 
3565           // similar is currently doing something (showing a 
3566           // message box, for instance). 
3570     if ( m_iFlags 
& wxPG_FL_IN_HANDLECUSTOMEDITOREVENT 
) 
3573     wxVariant 
pendingValue(selected
->GetValueRef()); 
3574     wxWindow
* wnd 
= GetEditorControl(); 
3575     wxWindow
* editorWnd 
= wxDynamicCast(event
.GetEventObject(), wxWindow
); 
3577     bool wasUnspecified 
= selected
->IsValueUnspecified(); 
3578     int usesAutoUnspecified 
= selected
->UsesAutoUnspecified(); 
3579     bool valueIsPending 
= false; 
3581     m_chgInfo_changedProperty 
= NULL
; 
3583     m_iFlags 
&= ~wxPG_FL_VALUE_CHANGE_IN_EVENT
; 
3586     // Filter out excess wxTextCtrl modified events 
3587     if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED 
&& wnd 
) 
3589         if ( wnd
->IsKindOf(CLASSINFO(wxTextCtrl
)) ) 
3591             wxTextCtrl
* tc 
= (wxTextCtrl
*) wnd
; 
3593             wxString newTcValue 
= tc
->GetValue(); 
3594             if ( m_prevTcValue 
== newTcValue 
) 
3596             m_prevTcValue 
= newTcValue
; 
3598         else if ( wnd
->IsKindOf(CLASSINFO(wxComboCtrl
)) ) 
3600             wxComboCtrl
* cc 
= (wxComboCtrl
*) wnd
; 
3602             wxString newTcValue 
= cc
->GetTextCtrl()->GetValue(); 
3603             if ( m_prevTcValue 
== newTcValue 
) 
3605             m_prevTcValue 
= newTcValue
; 
3609     SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
); 
3611     bool validationFailure 
= false; 
3612     bool buttonWasHandled 
= false; 
3615     // Try common button handling 
3616     if ( m_wndEditor2 
&& event
.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED 
) 
3618         wxPGEditorDialogAdapter
* adapter 
= selected
->GetEditorDialog(); 
3622             buttonWasHandled 
= true; 
3623             // Store as res2, as previously (and still currently alternatively) 
3624             // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED 
3625             // in wxPGProperty::OnEvent(). 
3626             adapter
->ShowDialog( this, selected 
); 
3631     if ( !buttonWasHandled 
) 
3633         if ( wnd 
|| m_wndEditor2 
) 
3635             // First call editor class' event handler. 
3636             const wxPGEditor
* editor 
= selected
->GetEditorClass(); 
3638             if ( editor
->OnEvent( this, selected
, editorWnd
, event 
) ) 
3640                 // If changes, validate them 
3641                 if ( DoEditorValidate() ) 
3643                     if ( editor
->GetValueFromControl( pendingValue
, 
3646                         valueIsPending 
= true; 
3648                     // Mark value always as pending if validation is currently 
3649                     // failing and value was not unspecified 
3650                     if ( !valueIsPending 
&& 
3651                          !pendingValue
.IsNull() && 
3652                          m_validationInfo
.m_isFailing 
) 
3653                          valueIsPending 
= true; 
3657                     validationFailure 
= true; 
3662         // Then the property's custom handler (must be always called, unless 
3663         // validation failed). 
3664         if ( !validationFailure 
) 
3665             buttonWasHandled 
= selected
->OnEvent( this, editorWnd
, event 
); 
3668     // SetValueInEvent(), as called in one of the functions referred above 
3669     // overrides editor's value. 
3670     if ( m_iFlags 
& wxPG_FL_VALUE_CHANGE_IN_EVENT 
) 
3672         valueIsPending 
= true; 
3673         pendingValue 
= m_changeInEventValue
; 
3674         selFlags 
|= wxPG_SEL_DIALOGVAL
; 
3677     if ( !validationFailure 
&& valueIsPending 
) 
3678         if ( !PerformValidation(selected
, pendingValue
) ) 
3679             validationFailure 
= true; 
3681     if ( validationFailure
) 
3683         OnValidationFailure(selected
, pendingValue
); 
3685     else if ( valueIsPending 
) 
3687         selFlags 
|= ( !wasUnspecified 
&& selected
->IsValueUnspecified() && usesAutoUnspecified 
) ? wxPG_SEL_SETUNSPEC 
: 0; 
3689         DoPropertyChanged(selected
, selFlags
); 
3690         EditorsValueWasNotModified(); 
3692         // Regardless of editor type, unfocus editor on 
3693         // text-editing related enter press. 
3694         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3701         // No value after all 
3703         // Regardless of editor type, unfocus editor on 
3704         // text-editing related enter press. 
3705         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3710         // Let unhandled button click events go to the parent 
3711         if ( !buttonWasHandled 
&& event
.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED 
) 
3713             wxCommandEvent 
evt(wxEVT_COMMAND_BUTTON_CLICKED
,GetId()); 
3714             GetEventHandler()->AddPendingEvent(evt
); 
3718     ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
); 
3721 // ----------------------------------------------------------------------- 
3722 // wxPropertyGrid editor control helper methods 
3723 // ----------------------------------------------------------------------- 
3725 wxRect 
wxPropertyGrid::GetEditorWidgetRect( wxPGProperty
* p
, int column 
) const 
3727     int itemy 
= p
->GetY2(m_lineHeight
); 
3728     int splitterX 
= m_pState
->DoGetSplitterPosition(column
-1); 
3729     int colEnd 
= splitterX 
+ m_pState
->m_colWidths
[column
]; 
3730     int imageOffset 
= 0; 
3732     int vx
, vy
;  // Top left corner of client 
3733     GetViewStart(&vx
, &vy
); 
3734     vy 
*= wxPG_PIXELS_PER_UNIT
; 
3738         // TODO: If custom image detection changes from current, change this. 
3739         if ( m_iFlags 
& wxPG_FL_CUR_USES_CUSTOM_IMAGE 
) 
3741             //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; 
3742             int iw 
= p
->OnMeasureImage().x
; 
3744                 iw 
= wxPG_CUSTOM_IMAGE_WIDTH
; 
3745             imageOffset 
= p
->GetImageOffset(iw
); 
3748     else if ( column 
== 0 ) 
3750         splitterX 
+= (p
->m_depth 
- 1) * m_subgroup_extramargin
; 
3755         splitterX
+imageOffset
+wxPG_XBEFOREWIDGET
+wxPG_CONTROL_MARGIN
+1, 
3757         colEnd
-splitterX
-wxPG_XBEFOREWIDGET
-wxPG_CONTROL_MARGIN
-imageOffset
-1, 
3762 // ----------------------------------------------------------------------- 
3764 wxRect 
wxPropertyGrid::GetImageRect( wxPGProperty
* p
, int item 
) const 
3766     wxSize sz 
= GetImageSize(p
, item
); 
3767     return wxRect(wxPG_CONTROL_MARGIN 
+ wxCC_CUSTOM_IMAGE_MARGIN1
, 
3768                   wxPG_CUSTOM_IMAGE_SPACINGY
, 
3773 // return size of custom paint image 
3774 wxSize 
wxPropertyGrid::GetImageSize( wxPGProperty
* p
, int item 
) const 
3776     // If called with NULL property, then return default image 
3777     // size for properties that use image. 
3779         return wxSize(wxPG_CUSTOM_IMAGE_WIDTH
,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
)); 
3781     wxSize cis 
= p
->OnMeasureImage(item
); 
3783     int choiceCount 
= p
->m_choices
.GetCount(); 
3784     int comVals 
= p
->GetDisplayedCommonValueCount(); 
3785     if ( item 
>= choiceCount 
&& comVals 
> 0 ) 
3787         unsigned int cvi 
= item
-choiceCount
; 
3788         cis 
= GetCommonValue(cvi
)->GetRenderer()->GetImageSize(NULL
, 1, cvi
); 
3790     else if ( item 
>= 0 && choiceCount 
== 0 ) 
3791         return wxSize(0, 0); 
3796             cis
.x 
= wxPG_CUSTOM_IMAGE_WIDTH
; 
3801             cis
.y 
= wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
); 
3808 // ----------------------------------------------------------------------- 
3810 // takes scrolling into account 
3811 void wxPropertyGrid::ImprovedClientToScreen( int* px
, int* py 
) 
3814     GetViewStart(&vx
,&vy
); 
3815     vy
*=wxPG_PIXELS_PER_UNIT
; 
3816     vx
*=wxPG_PIXELS_PER_UNIT
; 
3819     ClientToScreen( px
, py 
); 
3822 // ----------------------------------------------------------------------- 
3824 wxPropertyGridHitTestResult 
wxPropertyGrid::HitTest( const wxPoint
& pt 
) const 
3827     GetViewStart(&pt2
.x
,&pt2
.y
); 
3828     pt2
.x 
*= wxPG_PIXELS_PER_UNIT
; 
3829     pt2
.y 
*= wxPG_PIXELS_PER_UNIT
; 
3833     return m_pState
->HitTest(pt2
); 
3836 // ----------------------------------------------------------------------- 
3838 // custom set cursor 
3839 void wxPropertyGrid::CustomSetCursor( int type
, bool override 
) 
3841     if ( type 
== m_curcursor 
&& !override 
) return; 
3843     wxCursor
* cursor 
= &wxPG_DEFAULT_CURSOR
; 
3845     if ( type 
== wxCURSOR_SIZEWE 
) 
3846         cursor 
= m_cursorSizeWE
; 
3848     SetCursor( *cursor 
); 
3853 // ----------------------------------------------------------------------- 
3856 wxPropertyGrid::GetUnspecifiedValueText( int argFlags 
) const 
3858     const wxPGCell
& ua 
= GetUnspecifiedValueAppearance(); 
3860     if ( ua
.HasText() && 
3861          !(argFlags 
& wxPG_FULL_VALUE
) && 
3862          !(argFlags 
& wxPG_EDITABLE_VALUE
) ) 
3863         return ua
.GetText(); 
3865     return wxEmptyString
; 
3868 // ----------------------------------------------------------------------- 
3869 // wxPropertyGrid property selection, editor creation 
3870 // ----------------------------------------------------------------------- 
3873 // This class forwards events from property editor controls to wxPropertyGrid. 
3874 class wxPropertyGridEditorEventForwarder 
: public wxEvtHandler
 
3877     wxPropertyGridEditorEventForwarder( wxPropertyGrid
* propGrid 
) 
3878         : wxEvtHandler(), m_propGrid(propGrid
) 
3882     virtual ~wxPropertyGridEditorEventForwarder() 
3887     bool ProcessEvent( wxEvent
& event 
) 
3892         m_propGrid
->HandleCustomEditorEvent(event
); 
3895         // NB: On wxMSW, a wxTextCtrl with wxTE_PROCESS_ENTER 
3896         //     may beep annoyingly if that event is skipped 
3897         //     and passed to parent event handler. 
3898         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3901         return wxEvtHandler::ProcessEvent(event
); 
3904     wxPropertyGrid
*         m_propGrid
; 
3907 // Setups event handling for child control 
3908 void wxPropertyGrid::SetupChildEventHandling( wxWindow
* argWnd 
) 
3910     wxWindowID id 
= argWnd
->GetId(); 
3912     if ( argWnd 
== m_wndEditor 
) 
3914         argWnd
->Connect(id
, wxEVT_MOTION
, 
3915             wxMouseEventHandler(wxPropertyGrid::OnMouseMoveChild
), 
3917         argWnd
->Connect(id
, wxEVT_LEFT_UP
, 
3918             wxMouseEventHandler(wxPropertyGrid::OnMouseUpChild
), 
3920         argWnd
->Connect(id
, wxEVT_LEFT_DOWN
, 
3921             wxMouseEventHandler(wxPropertyGrid::OnMouseClickChild
), 
3923         argWnd
->Connect(id
, wxEVT_RIGHT_UP
, 
3924             wxMouseEventHandler(wxPropertyGrid::OnMouseRightClickChild
), 
3926         argWnd
->Connect(id
, wxEVT_ENTER_WINDOW
, 
3927             wxMouseEventHandler(wxPropertyGrid::OnMouseEntry
), 
3929         argWnd
->Connect(id
, wxEVT_LEAVE_WINDOW
, 
3930             wxMouseEventHandler(wxPropertyGrid::OnMouseEntry
), 
3934     wxPropertyGridEditorEventForwarder
* forwarder
; 
3935     forwarder 
= new wxPropertyGridEditorEventForwarder(this); 
3936     argWnd
->PushEventHandler(forwarder
); 
3938     argWnd
->Connect(id
, wxEVT_KEY_DOWN
, 
3939         wxCharEventHandler(wxPropertyGrid::OnChildKeyDown
), 
3943 void wxPropertyGrid::DestroyEditorWnd( wxWindow
* wnd 
) 
3950     // Do not free editors immediately (for sake of processing events) 
3951     wxPendingDelete
.Append(wnd
); 
3954 void wxPropertyGrid::FreeEditors() 
3957     // Return focus back to canvas from children (this is required at least for 
3958     // GTK+, which, unlike Windows, clears focus when control is destroyed 
3959     // instead of moving it to closest parent). 
3960     wxWindow
* focus 
= wxWindow::FindFocus(); 
3963         wxWindow
* parent 
= focus
->GetParent(); 
3966             if ( parent 
== this ) 
3971             parent 
= parent
->GetParent(); 
3975     // Do not free editors immediately if processing events 
3978         wxEvtHandler
* handler 
= m_wndEditor2
->PopEventHandler(false); 
3979         m_wndEditor2
->Hide(); 
3980         wxPendingDelete
.Append( handler 
); 
3981         DestroyEditorWnd(m_wndEditor2
); 
3982         m_wndEditor2 
= NULL
; 
3987         wxEvtHandler
* handler 
= m_wndEditor
->PopEventHandler(false); 
3988         m_wndEditor
->Hide(); 
3989         wxPendingDelete
.Append( handler 
); 
3990         DestroyEditorWnd(m_wndEditor
); 
3995 // Call with NULL to de-select property 
3996 bool wxPropertyGrid::DoSelectProperty( wxPGProperty
* p
, unsigned int flags 
) 
4001         wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(), 
4002             p->m_parent->m_label.c_str(),p->GetIndexInParent()); 
4006         wxLogDebug(wxT("SelectProperty( NULL, -1 )")); 
4010     if ( m_inDoSelectProperty 
) 
4013     m_inDoSelectProperty 
= true; 
4014     wxON_BLOCK_EXIT_SET(m_inDoSelectProperty
, false); 
4019     wxArrayPGProperty prevSelection 
= m_pState
->m_selection
; 
4020     wxPGProperty
* prevFirstSel
; 
4022     if ( prevSelection
.size() > 0 ) 
4023         prevFirstSel 
= prevSelection
[0]; 
4025         prevFirstSel 
= NULL
; 
4027     if ( prevFirstSel 
&& prevFirstSel
->HasFlag(wxPG_PROP_BEING_DELETED
) ) 
4028         prevFirstSel 
= NULL
; 
4030     // Always send event, as this is indirect call 
4031     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
); 
4035         wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() ); 
4037         wxPrintf( "None selected\n" ); 
4040         wxPrintf( "P =  %s\n", p->GetClassInfo()->GetClassName() ); 
4042         wxPrintf( "P = NULL\n" ); 
4045     wxWindow
* primaryCtrl 
= NULL
; 
4047     // If we are frozen, then just set the values. 
4050         m_iFlags 
&= ~(wxPG_FL_ABNORMAL_EDITOR
); 
4051         m_editorFocused 
= 0; 
4052         m_pState
->DoSetSelection(p
); 
4054         // If frozen, always free controls. But don't worry, as Thaw will 
4055         // recall SelectProperty to recreate them. 
4058         // Prevent any further selection measures in this call 
4064         if ( prevFirstSel 
== p 
&& 
4065              prevSelection
.size() <= 1 && 
4066              !(flags 
& wxPG_SEL_FORCE
) ) 
4068             // Only set focus if not deselecting 
4071                 if ( flags 
& wxPG_SEL_FOCUS 
) 
4075                         m_wndEditor
->SetFocus(); 
4076                         m_editorFocused 
= 1; 
4089         // First, deactivate previous 
4092             // Must double-check if this is an selected in case of forceswitch 
4093             if ( p 
!= prevFirstSel 
) 
4095                 if ( !CommitChangesFromEditor(flags
) ) 
4097                     // Validation has failed, so we can't exit the previous editor 
4098                     //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."), 
4099                     //               _("Invalid Value"),wxOK|wxICON_ERROR); 
4104             // This should be called after CommitChangesFromEditor(), so that 
4105             // OnValidationFailure() still has information on property's 
4106             // validation state. 
4107             OnValidationFailureReset(prevFirstSel
); 
4111             m_iFlags 
&= ~(wxPG_FL_ABNORMAL_EDITOR
); 
4112             EditorsValueWasNotModified(); 
4115         SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY
); 
4117         m_pState
->DoSetSelection(p
); 
4119         // Redraw unselected 
4120         for ( unsigned int i
=0; i
<prevSelection
.size(); i
++ ) 
4122             DrawItem(prevSelection
[i
]); 
4126         // Then, activate the one given. 
4129             int propY 
= p
->GetY2(m_lineHeight
); 
4131             int splitterX 
= GetSplitterPosition(); 
4132             m_editorFocused 
= 0; 
4133             m_iFlags 
|= wxPG_FL_PRIMARY_FILLS_ENTIRE
; 
4135             wxASSERT( m_wndEditor 
== NULL 
); 
4138             // Only create editor for non-disabled non-caption 
4139             if ( !p
->IsCategory() && !(p
->m_flags 
& wxPG_PROP_DISABLED
) ) 
4141             // do this for non-caption items 
4145                 // Do we need to paint the custom image, if any? 
4146                 m_iFlags 
&= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE
); 
4147                 if ( (p
->m_flags 
& wxPG_PROP_CUSTOMIMAGE
) && 
4148                      !p
->GetEditorClass()->CanContainCustomImage() 
4150                     m_iFlags 
|= wxPG_FL_CUR_USES_CUSTOM_IMAGE
; 
4152                 wxRect grect 
= GetEditorWidgetRect(p
, m_selColumn
); 
4153                 wxPoint goodPos 
= grect
.GetPosition(); 
4155                 // Editor appearance can now be considered clear 
4156                 m_editorAppearance
.SetEmptyData(); 
4158                 const wxPGEditor
* editor 
= p
->GetEditorClass(); 
4159                 wxCHECK_MSG(editor
, false, 
4160                     wxT("NULL editor class not allowed")); 
4162                 m_iFlags 
&= ~wxPG_FL_FIXED_WIDTH_EDITOR
; 
4164                 wxPGWindowList wndList 
= 
4165                     editor
->CreateControls(this, 
4170                 m_wndEditor 
= wndList
.m_primary
; 
4171                 m_wndEditor2 
= wndList
.m_secondary
; 
4172                 primaryCtrl 
= GetEditorControl(); 
4175                 // Essentially, primaryCtrl == m_wndEditor 
4178                 // NOTE: It is allowed for m_wndEditor to be NULL - in this 
4179                 //       case value is drawn as normal, and m_wndEditor2 is 
4180                 //       assumed to be a right-aligned button that triggers 
4181                 //       a separate editorCtrl window. 
4185                     wxASSERT_MSG( m_wndEditor
->GetParent() == GetPanel(), 
4186                                   "CreateControls must use result of " 
4187                                   "wxPropertyGrid::GetPanel() as parent " 
4190                     // Set validator, if any 
4191                 #if wxUSE_VALIDATORS 
4192                     wxValidator
* validator 
= p
->GetValidator(); 
4194                         primaryCtrl
->SetValidator(*validator
); 
4197                     if ( m_wndEditor
->GetSize().y 
> (m_lineHeight
+6) ) 
4198                         m_iFlags 
|= wxPG_FL_ABNORMAL_EDITOR
; 
4200                     // If it has modified status, use bold font 
4201                     // (must be done before capturing m_ctrlXAdjust) 
4202                     if ( (p
->m_flags 
& wxPG_PROP_MODIFIED
) && 
4203                          (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
4204                         SetCurControlBoldFont(); 
4206                     // Store x relative to splitter (we'll need it). 
4207                     m_ctrlXAdjust 
= m_wndEditor
->GetPosition().x 
- splitterX
; 
4209                     // Check if background clear is not necessary 
4210                     wxPoint pos 
= m_wndEditor
->GetPosition(); 
4211                     if ( pos
.x 
> (splitterX
+1) || pos
.y 
> propY 
) 
4213                         m_iFlags 
&= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE
); 
4216                     m_wndEditor
->SetSizeHints(3, 3); 
4218                     SetupChildEventHandling(primaryCtrl
); 
4220                     // Focus and select all (wxTextCtrl, wxComboBox etc) 
4221                     if ( flags 
& wxPG_SEL_FOCUS 
) 
4223                         primaryCtrl
->SetFocus(); 
4225                         p
->GetEditorClass()->OnFocus(p
, primaryCtrl
); 
4229                         if ( p
->IsValueUnspecified() ) 
4230                             SetEditorAppearance(m_unspecifiedAppearance
, 
4237                     wxASSERT_MSG( m_wndEditor2
->GetParent() == GetPanel(), 
4238                                   "CreateControls must use result of " 
4239                                   "wxPropertyGrid::GetPanel() as parent " 
4242                     // Get proper id for wndSecondary 
4243                     m_wndSecId 
= m_wndEditor2
->GetId(); 
4244                     wxWindowList children 
= m_wndEditor2
->GetChildren(); 
4245                     wxWindowList::iterator node 
= children
.begin(); 
4246                     if ( node 
!= children
.end() ) 
4247                         m_wndSecId 
= ((wxWindow
*)*node
)->GetId(); 
4249                     m_wndEditor2
->SetSizeHints(3,3); 
4251                     m_wndEditor2
->Show(); 
4253                     SetupChildEventHandling(m_wndEditor2
); 
4255                     // If no primary editor, focus to button to allow 
4256                     // it to interprete ENTER etc. 
4257                     // NOTE: Due to problems focusing away from it, this 
4258                     //       has been disabled. 
4260                     if ( (flags & wxPG_SEL_FOCUS) && !m_wndEditor ) 
4261                         m_wndEditor2->SetFocus(); 
4265                 if ( flags 
& wxPG_SEL_FOCUS 
) 
4266                     m_editorFocused 
= 1; 
4271                 // Make sure focus is in grid canvas (important for wxGTK, 
4276             EditorsValueWasNotModified(); 
4278             // If it's inside collapsed section, expand parent, scroll, etc. 
4279             // Also, if it was partially visible, scroll it into view. 
4280             if ( !(flags 
& wxPG_SEL_NONVISIBLE
) ) 
4285                 m_wndEditor
->Show(true); 
4288             if ( !(flags 
& wxPG_SEL_NO_REFRESH
) ) 
4293             // Make sure focus is in grid canvas 
4297         ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY
); 
4300     const wxString
* pHelpString 
= NULL
; 
4303         pHelpString 
= &p
->GetHelpString(); 
4305     if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS
) ) 
4310         // Show help text in status bar. 
4311         //   (if found and grid not embedded in manager with help box and 
4312         //    style wxPG_EX_HELP_AS_TOOLTIPS is not used). 
4314         wxStatusBar
* statusbar 
= GetStatusBar(); 
4317             if ( pHelpString 
&& pHelpString
->length() ) 
4319                 // Set help box text. 
4320                 statusbar
->SetStatusText( *pHelpString 
); 
4321                 m_iFlags 
|= wxPG_FL_STRING_IN_STATUSBAR
; 
4323             else if ( m_iFlags 
& wxPG_FL_STRING_IN_STATUSBAR 
) 
4325                 // Clear help box - but only if it was written 
4326                 // by us at previous time. 
4327                 statusbar
->SetStatusText( m_emptyString 
); 
4328                 m_iFlags 
&= ~(wxPG_FL_STRING_IN_STATUSBAR
); 
4335 #if wxPG_SUPPORT_TOOLTIPS 
4337         // Show help as a tool tip on the editor control. 
4339         if ( pHelpString 
&& pHelpString
->length() && 
4342             primaryCtrl
->SetToolTip(*pHelpString
); 
4347     // call wx event handler (here so that it also occurs on deselection) 
4348     if ( !(flags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
4349         SendEvent( wxEVT_PG_SELECTED
, p
, NULL 
); 
4354 // ----------------------------------------------------------------------- 
4356 bool wxPropertyGrid::UnfocusEditor() 
4358     wxPGProperty
* selected 
= GetSelection(); 
4360     if ( !selected 
|| !m_wndEditor 
|| m_frozen 
) 
4363     if ( !CommitChangesFromEditor(0) ) 
4372 // ----------------------------------------------------------------------- 
4374 void wxPropertyGrid::RefreshEditor() 
4376     wxPGProperty
* p 
= GetSelection(); 
4380     wxWindow
* wnd 
= GetEditorControl(); 
4384     // Set editor font boldness - must do this before 
4385     // calling UpdateControl(). 
4386     if ( HasFlag(wxPG_BOLD_MODIFIED
) ) 
4388         if ( p
->HasFlag(wxPG_PROP_MODIFIED
) ) 
4389             wnd
->SetFont(GetCaptionFont()); 
4391             wnd
->SetFont(GetFont()); 
4394     const wxPGEditor
* editorClass 
= p
->GetEditorClass(); 
4396     editorClass
->UpdateControl(p
, wnd
); 
4398     if ( p
->IsValueUnspecified() ) 
4399         SetEditorAppearance(m_unspecifiedAppearance
, true); 
4402 // ----------------------------------------------------------------------- 
4404 bool wxPropertyGrid::SelectProperty( wxPGPropArg id
, bool focus 
) 
4406     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
4408     int flags 
= wxPG_SEL_DONT_SEND_EVENT
; 
4410         flags 
|= wxPG_SEL_FOCUS
; 
4412     return DoSelectProperty(p
, flags
); 
4415 // ----------------------------------------------------------------------- 
4416 // wxPropertyGrid expand/collapse state 
4417 // ----------------------------------------------------------------------- 
4419 bool wxPropertyGrid::DoCollapse( wxPGProperty
* p
, bool sendEvents 
) 
4421     wxPGProperty
* pwc 
= wxStaticCast(p
, wxPGProperty
); 
4422     wxPGProperty
* selected 
= GetSelection(); 
4424     // If active editor was inside collapsed section, then disable it 
4425     if ( selected 
&& selected
->IsSomeParent(p
) ) 
4430     // Store dont-center-splitter flag 'cause we need to temporarily set it 
4431     bool prevDontCenterSplitter 
= m_pState
->m_dontCenterSplitter
; 
4432     m_pState
->m_dontCenterSplitter 
= true; 
4434     bool res 
= m_pState
->DoCollapse(pwc
); 
4439             SendEvent( wxEVT_PG_ITEM_COLLAPSED
, p 
); 
4441         RecalculateVirtualSize(); 
4445     m_pState
->m_dontCenterSplitter 
= prevDontCenterSplitter
; 
4450 // ----------------------------------------------------------------------- 
4452 bool wxPropertyGrid::DoExpand( wxPGProperty
* p
, bool sendEvents 
) 
4454     wxCHECK_MSG( p
, false, wxT("invalid property id") ); 
4456     wxPGProperty
* pwc 
= (wxPGProperty
*)p
; 
4458     // Store dont-center-splitter flag 'cause we need to temporarily set it 
4459     bool prevDontCenterSplitter 
= m_pState
->m_dontCenterSplitter
; 
4460     m_pState
->m_dontCenterSplitter 
= true; 
4462     bool res 
= m_pState
->DoExpand(pwc
); 
4467             SendEvent( wxEVT_PG_ITEM_EXPANDED
, p 
); 
4469         RecalculateVirtualSize(); 
4473     m_pState
->m_dontCenterSplitter 
= prevDontCenterSplitter
; 
4478 // ----------------------------------------------------------------------- 
4480 bool wxPropertyGrid::DoHideProperty( wxPGProperty
* p
, bool hide
, int flags 
) 
4483         return m_pState
->DoHideProperty(p
, hide
, flags
); 
4485     wxArrayPGProperty selection 
= m_pState
->m_selection
;  // Must use a copy 
4486     int selRemoveCount 
= 0; 
4487     for ( unsigned int i
=0; i
<selection
.size(); i
++ ) 
4489         wxPGProperty
* selected 
= selection
[i
]; 
4490         if ( selected 
== p 
|| selected
->IsSomeParent(p
) ) 
4492             if ( !DoRemoveFromSelection(p
, flags
) ) 
4494             selRemoveCount 
+= 1; 
4498     m_pState
->DoHideProperty(p
, hide
, flags
); 
4500     RecalculateVirtualSize(); 
4507 // ----------------------------------------------------------------------- 
4508 // wxPropertyGrid size related methods 
4509 // ----------------------------------------------------------------------- 
4511 void wxPropertyGrid::RecalculateVirtualSize( int forceXPos 
) 
4513     // Don't check for !HasInternalFlag(wxPG_FL_INITIALIZED) here. Otherwise 
4514     // virtual size calculation may go wrong. 
4515     if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE
) || 
4521     // If virtual height was changed, then recalculate editor control position(s) 
4522     if ( m_pState
->m_vhCalcPending 
) 
4523         CorrectEditorWidgetPosY(); 
4525     m_pState
->EnsureVirtualHeight(); 
4527     wxASSERT_LEVEL_2_MSG( 
4528         m_pState
->GetVirtualHeight() == m_pState
->GetActualVirtualHeight(), 
4529         "VirtualHeight and ActualVirtualHeight should match" 
4532     m_iFlags 
|= wxPG_FL_RECALCULATING_VIRTUAL_SIZE
; 
4534     int x 
= m_pState
->m_width
; 
4535     int y 
= m_pState
->m_virtualHeight
; 
4538     GetClientSize(&width
,&height
); 
4540     // Now adjust virtual size. 
4541     SetVirtualSize(x
, y
); 
4547     // Adjust scrollbars 
4548     if ( HasVirtualWidth() ) 
4550         xAmount 
= x
/wxPG_PIXELS_PER_UNIT
; 
4551         xPos 
= GetScrollPos( wxHORIZONTAL 
); 
4554     if ( forceXPos 
!= -1 ) 
4557     else if ( xPos 
> (xAmount
-(width
/wxPG_PIXELS_PER_UNIT
)) ) 
4560     int yAmount 
= y 
/ wxPG_PIXELS_PER_UNIT
; 
4561     int yPos 
= GetScrollPos( wxVERTICAL 
); 
4563     SetScrollbars( wxPG_PIXELS_PER_UNIT
, wxPG_PIXELS_PER_UNIT
, 
4564                    xAmount
, yAmount
, xPos
, yPos
, true ); 
4566     // Must re-get size now 
4567     GetClientSize(&width
,&height
); 
4569     if ( !HasVirtualWidth() ) 
4571         m_pState
->SetVirtualWidth(width
); 
4578     m_pState
->CheckColumnWidths(); 
4580     if ( GetSelection() ) 
4581         CorrectEditorWidgetSizeX(); 
4583     m_iFlags 
&= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE
; 
4586 // ----------------------------------------------------------------------- 
4588 void wxPropertyGrid::OnResize( wxSizeEvent
& event 
) 
4590     if ( !(m_iFlags 
& wxPG_FL_INITIALIZED
) ) 
4594     GetClientSize(&width
, &height
); 
4599 #if wxPG_DOUBLE_BUFFER 
4600     if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING
) ) 
4602         int dblh 
= (m_lineHeight
*2); 
4603         if ( !m_doubleBuffer 
) 
4605             // Create double buffer bitmap to draw on, if none 
4606             int w 
= (width
>250)?width
:250; 
4607             int h 
= height 
+ dblh
; 
4609             m_doubleBuffer 
= new wxBitmap( w
, h 
); 
4613             int w 
= m_doubleBuffer
->GetWidth(); 
4614             int h 
= m_doubleBuffer
->GetHeight(); 
4616             // Double buffer must be large enough 
4617             if ( w 
< width 
|| h 
< (height
+dblh
) ) 
4619                 if ( w 
< width 
) w 
= width
; 
4620                 if ( h 
< (height
+dblh
) ) h 
= height 
+ dblh
; 
4621                 delete m_doubleBuffer
; 
4622                 m_doubleBuffer 
= new wxBitmap( w
, h 
); 
4629     m_pState
->OnClientWidthChange( width
, event
.GetSize().x 
- m_ncWidth
, true ); 
4630     m_ncWidth 
= event
.GetSize().x
; 
4634         if ( m_pState
->m_itemsAdded 
) 
4635             PrepareAfterItemsAdded(); 
4637             // Without this, virtual size (atleast under wxGTK) will be skewed 
4638             RecalculateVirtualSize(); 
4644 // ----------------------------------------------------------------------- 
4646 void wxPropertyGrid::SetVirtualWidth( int width 
) 
4650         // Disable virtual width 
4651         width 
= GetClientSize().x
; 
4652         ClearInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH
); 
4656         // Enable virtual width 
4657         SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH
); 
4659     m_pState
->SetVirtualWidth( width 
); 
4662 void wxPropertyGrid::SetFocusOnCanvas() 
4665     m_editorFocused 
= 0; 
4668 // ----------------------------------------------------------------------- 
4669 // wxPropertyGrid mouse event handling 
4670 // ----------------------------------------------------------------------- 
4672 // selFlags uses same values DoSelectProperty's flags 
4673 // Returns true if event was vetoed. 
4674 bool wxPropertyGrid::SendEvent( int eventType
, wxPGProperty
* p
, 
4676                                 unsigned int selFlags
, 
4677                                 unsigned int column 
) 
4679     // selFlags should have wxPG_SEL_NOVALIDATE if event is not 
4682     // Send property grid event of specific type and with specific property 
4683     wxPropertyGridEvent 
evt( eventType
, m_eventObject
->GetId() ); 
4684     evt
.SetPropertyGrid(this); 
4685     evt
.SetEventObject(m_eventObject
); 
4687     evt
.SetColumn(column
); 
4688     if ( eventType 
== wxEVT_PG_CHANGING 
) 
4691         evt
.SetCanVeto(true); 
4692         m_validationInfo
.m_pValue 
= pValue
; 
4693         evt
.SetupValidationInfo(); 
4698             evt
.SetPropertyValue(p
->GetValue()); 
4700         if ( !(selFlags 
& wxPG_SEL_NOVALIDATE
) ) 
4701             evt
.SetCanVeto(true); 
4704     wxPropertyGridEvent
* prevProcessedEvent 
= m_processedEvent
; 
4705     m_processedEvent 
= &evt
; 
4706     m_eventObject
->HandleWindowEvent(evt
); 
4707     m_processedEvent 
= prevProcessedEvent
; 
4709     return evt
.WasVetoed(); 
4712 // ----------------------------------------------------------------------- 
4714 // Return false if should be skipped 
4715 bool wxPropertyGrid::HandleMouseClick( int x
, unsigned int y
, wxMouseEvent 
&event 
) 
4719     // Need to set focus? 
4720     if ( !(m_iFlags 
& wxPG_FL_FOCUSED
) ) 
4725     wxPropertyGridPageState
* state 
= m_pState
; 
4727     int splitterHitOffset
; 
4728     int columnHit 
= state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4730     wxPGProperty
* p 
= DoGetItemAtY(y
); 
4734         int depth 
= (int)p
->GetDepth() - 1; 
4736         int marginEnds 
= m_marginWidth 
+ ( depth 
* m_subgroup_extramargin 
); 
4738         if ( x 
>= marginEnds 
) 
4742             if ( p
->IsCategory() ) 
4744                 // This is category. 
4745                 wxPropertyCategory
* pwc 
= (wxPropertyCategory
*)p
; 
4747                 int textX 
= m_marginWidth 
+ ((unsigned int)((pwc
->m_depth
-1)*m_subgroup_extramargin
)); 
4749                 // Expand, collapse, activate etc. if click on text or left of splitter. 
4752                      ( x 
< (textX
+pwc
->GetTextExtent(this, m_captionFont
)+(wxPG_CAPRECTXMARGIN
*2)) || 
4757                     if ( !AddToSelectionFromInputEvent( p
, 
4762                     // On double-click, expand/collapse. 
4763                     if ( event
.ButtonDClick() && !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
4765                         if ( pwc
->IsExpanded() ) DoCollapse( p
, true ); 
4766                         else DoExpand( p
, true ); 
4770             else if ( splitterHit 
== -1 ) 
4773                 unsigned int selFlag 
= 0; 
4774                 if ( columnHit 
== 1 ) 
4776                     m_iFlags 
|= wxPG_FL_ACTIVATION_BY_CLICK
; 
4777                     selFlag 
= wxPG_SEL_FOCUS
; 
4779                 if ( !AddToSelectionFromInputEvent( p
, 
4785                 m_iFlags 
&= ~(wxPG_FL_ACTIVATION_BY_CLICK
); 
4787                 if ( p
->GetChildCount() && !p
->IsCategory() ) 
4788                     // On double-click, expand/collapse. 
4789                     if ( event
.ButtonDClick() && !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
4791                         wxPGProperty
* pwc 
= (wxPGProperty
*)p
; 
4792                         if ( pwc
->IsExpanded() ) DoCollapse( p
, true ); 
4793                         else DoExpand( p
, true ); 
4796                 // Do not Skip() the event after selection has been made. 
4797                 // Otherwise default event handling behavior kicks in 
4798                 // and may revert focus back to the main canvas. 
4803             // click on splitter 
4804                 if ( !(m_windowStyle 
& wxPG_STATIC_SPLITTER
) ) 
4806                     if ( event
.GetEventType() == wxEVT_LEFT_DCLICK 
) 
4808                         // Double-clicking the splitter causes auto-centering 
4809                         if ( m_pState
->GetColumnCount() <= 2 ) 
4811                             ResetColumnSizes( true ); 
4813                             SendEvent(wxEVT_PG_COL_DRAGGING
, 
4816                                       wxPG_SEL_NOVALIDATE
, 
4817                                       (unsigned int)m_draggedSplitter
); 
4820                     else if ( m_dragStatus 
== 0 ) 
4823                         // Begin draggin the splitter 
4827                         DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
); 
4829                         // Allow application to veto dragging 
4830                         if ( !SendEvent(wxEVT_PG_COL_BEGIN_DRAG
, 
4832                                         (unsigned int)splitterHit
) ) 
4836                                 // Changes must be committed here or the 
4837                                 // value won't be drawn correctly 
4838                                 if ( !CommitChangesFromEditor() ) 
4841                                 m_wndEditor
->Show ( false ); 
4844                             if ( !(m_iFlags 
& wxPG_FL_MOUSE_CAPTURED
) ) 
4847                                 m_iFlags 
|= wxPG_FL_MOUSE_CAPTURED
; 
4851                             m_draggedSplitter 
= splitterHit
; 
4852                             m_dragOffset 
= splitterHitOffset
; 
4854                         #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
4855                             // Fixes button disappearance bug 
4857                                 m_wndEditor2
->Show ( false ); 
4860                             m_startingSplitterX 
= x 
- splitterHitOffset
; 
4869             if ( p
->GetChildCount() ) 
4871                 int nx 
= x 
+ m_marginWidth 
- marginEnds
; // Normalize x. 
4873                 // Fine tune cell button x 
4874                 if ( !p
->IsCategory() ) 
4875                     nx 
-= IN_CELL_EXPANDER_BUTTON_X_ADJUST
; 
4877                 if ( (nx 
>= m_gutterWidth 
&& nx 
< (m_gutterWidth
+m_iconWidth
)) ) 
4879                     int y2 
= y 
% m_lineHeight
; 
4880                     if ( (y2 
>= m_buttonSpacingY 
&& y2 
< (m_buttonSpacingY
+m_iconHeight
)) ) 
4882                         // On click on expander button, expand/collapse 
4883                         if ( ((wxPGProperty
*)p
)->IsExpanded() ) 
4884                             DoCollapse( p
, true ); 
4886                             DoExpand( p
, true ); 
4895 // ----------------------------------------------------------------------- 
4897 bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x
), 
4898                                             unsigned int WXUNUSED(y
), 
4899                                             wxMouseEvent
& event 
) 
4903         // Select property here as well 
4904         wxPGProperty
* p 
= m_propHover
; 
4905         AddToSelectionFromInputEvent(p
, m_colHover
, &event
); 
4907         // Send right click event. 
4908         SendEvent( wxEVT_PG_RIGHT_CLICK
, p 
); 
4915 // ----------------------------------------------------------------------- 
4917 bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x
), 
4918                                              unsigned int WXUNUSED(y
), 
4919                                              wxMouseEvent
& event 
) 
4923         // Select property here as well 
4924         wxPGProperty
* p 
= m_propHover
; 
4926         AddToSelectionFromInputEvent(p
, m_colHover
, &event
); 
4928         // Send double-click event. 
4929         SendEvent( wxEVT_PG_DOUBLE_CLICK
, m_propHover 
); 
4936 // ----------------------------------------------------------------------- 
4938 // Return false if should be skipped 
4939 bool wxPropertyGrid::HandleMouseMove( int x
, unsigned int y
, 
4940                                       wxMouseEvent 
&event 
) 
4942     // Safety check (needed because mouse capturing may 
4943     // otherwise freeze the control) 
4944     if ( m_dragStatus 
> 0 && !event
.Dragging() ) 
4946         HandleMouseUp(x
, y
, event
); 
4949     wxPropertyGridPageState
* state 
= m_pState
; 
4951     int splitterHitOffset
; 
4952     int columnHit 
= state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4953     int splitterX 
= x 
- splitterHitOffset
; 
4955     m_colHover 
= columnHit
; 
4957     if ( m_dragStatus 
> 0 ) 
4959         if ( x 
> (m_marginWidth 
+ wxPG_DRAG_MARGIN
) && 
4960              x 
< (m_pState
->m_width 
- wxPG_DRAG_MARGIN
) ) 
4963             int newSplitterX 
= x 
- m_dragOffset
; 
4965             // Splitter redraw required? 
4966             if ( newSplitterX 
!= splitterX 
) 
4969                 DoSetSplitterPosition(newSplitterX
, 
4971                                       wxPG_SPLITTER_REFRESH 
| 
4972                                       wxPG_SPLITTER_FROM_EVENT
); 
4974                 SendEvent(wxEVT_PG_COL_DRAGGING
, 
4977                           wxPG_SEL_NOVALIDATE
, 
4978                           (unsigned int)m_draggedSplitter
); 
4989         int ih 
= m_lineHeight
; 
4992     #if wxPG_SUPPORT_TOOLTIPS 
4993         wxPGProperty
* prevHover 
= m_propHover
; 
4994         unsigned char prevSide 
= m_mouseSide
; 
4996         int curPropHoverY 
= y 
- (y 
% ih
); 
4998         // On which item it hovers 
5001              ( sy 
< m_propHoverY 
|| sy 
>= (m_propHoverY
+ih
) ) 
5004             // Mouse moves on another property 
5006             m_propHover 
= DoGetItemAtY(y
); 
5007             m_propHoverY 
= curPropHoverY
; 
5010             SendEvent( wxEVT_PG_HIGHLIGHTED
, m_propHover 
); 
5013     #if wxPG_SUPPORT_TOOLTIPS 
5014         // Store which side we are on 
5016         if ( columnHit 
== 1 ) 
5018         else if ( columnHit 
== 0 ) 
5022         // If tooltips are enabled, show label or value as a tip 
5023         // in case it doesn't otherwise show in full length. 
5025         if ( m_windowStyle 
& wxPG_TOOLTIPS 
) 
5027             if ( m_propHover 
!= prevHover 
|| prevSide 
!= m_mouseSide 
) 
5029                 if ( m_propHover 
&& !m_propHover
->IsCategory() ) 
5032                     if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS 
) 
5034                         // Show help string as a tooltip 
5035                         wxString tipString 
= m_propHover
->GetHelpString(); 
5037                         SetToolTip(tipString
); 
5041                         // Show cropped value string as a tooltip 
5045                         if ( m_mouseSide 
== 1 ) 
5047                             tipString 
= m_propHover
->m_label
; 
5048                             space 
= splitterX
-m_marginWidth
-3; 
5050                         else if ( m_mouseSide 
== 2 ) 
5052                             tipString 
= m_propHover
->GetDisplayedString(); 
5054                             space 
= m_width 
- splitterX
; 
5055                             if ( m_propHover
->m_flags 
& wxPG_PROP_CUSTOMIMAGE 
) 
5056                                 space 
-= wxPG_CUSTOM_IMAGE_WIDTH 
+ 
5057                                          wxCC_CUSTOM_IMAGE_MARGIN1 
+ 
5058                                          wxCC_CUSTOM_IMAGE_MARGIN2
; 
5064                             GetTextExtent( tipString
, &tw
, &th
, 0, 0 ); 
5066                                 SetToolTip( tipString 
); 
5070                             SetToolTip( m_emptyString 
); 
5077                     SetToolTip( m_emptyString 
); 
5083         if ( splitterHit 
== -1 || 
5085              HasFlag(wxPG_STATIC_SPLITTER
) ) 
5087             // hovering on something else 
5088             if ( m_curcursor 
!= wxCURSOR_ARROW 
) 
5089                 CustomSetCursor( wxCURSOR_ARROW 
); 
5093             // Do not allow splitter cursor on caption items. 
5094             // (also not if we were dragging and its started 
5095             // outside the splitter region) 
5097             if ( !m_propHover
->IsCategory() && 
5101                 // hovering on splitter 
5103                 // NB: Condition disabled since MouseLeave event (from the 
5104                 //     editor control) cannot be reliably detected. 
5105                 //if ( m_curcursor != wxCURSOR_SIZEWE ) 
5106                 CustomSetCursor( wxCURSOR_SIZEWE
, true ); 
5112                 // hovering on something else 
5113                 if ( m_curcursor 
!= wxCURSOR_ARROW 
) 
5114                     CustomSetCursor( wxCURSOR_ARROW 
); 
5119         // Multi select by dragging 
5121         if ( (GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION
) && 
5122              event
.LeftIsDown() && 
5126              !state
->DoIsPropertySelected(m_propHover
) ) 
5128             // Additional requirement is that the hovered property 
5129             // is adjacent to edges of selection. 
5130             const wxArrayPGProperty
& selection 
= GetSelectedProperties(); 
5132             // Since categories cannot be selected along with 'other' 
5133             // properties, exclude them from iterator flags. 
5134             int iterFlags 
= wxPG_ITERATE_VISIBLE 
& (~wxPG_PROP_CATEGORY
); 
5136             for ( int i
=(selection
.size()-1); i
>=0; i
-- ) 
5138                 // TODO: This could be optimized by keeping track of 
5139                 //       which properties are at the edges of selection. 
5140                 wxPGProperty
* selProp 
= selection
[i
]; 
5141                 if ( state
->ArePropertiesAdjacent(m_propHover
, selProp
, 
5144                     DoAddToSelection(m_propHover
); 
5153 // ----------------------------------------------------------------------- 
5155 // Also handles Leaving event 
5156 bool wxPropertyGrid::HandleMouseUp( int x
, unsigned int WXUNUSED(y
), 
5157                                     wxMouseEvent 
&WXUNUSED(event
) ) 
5159     wxPropertyGridPageState
* state 
= m_pState
; 
5163     int splitterHitOffset
; 
5164     state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
5166     // No event type check - basicly calling this method should 
5167     // just stop dragging. 
5168     // Left up after dragged? 
5169     if ( m_dragStatus 
>= 1 ) 
5172     // End Splitter Dragging 
5174         // DO NOT ENABLE FOLLOWING LINE! 
5175         // (it is only here as a reminder to not to do it) 
5178         SendEvent(wxEVT_PG_COL_END_DRAG
, 
5181                   wxPG_SEL_NOVALIDATE
, 
5182                   (unsigned int)m_draggedSplitter
); 
5184         // Disable splitter auto-centering (but only if moved any - 
5185         // otherwise we end up disabling auto-center even after a 
5186         // recentering double-click). 
5187         int posDiff 
= abs(m_startingSplitterX 
-  
5188                           GetSplitterPosition(m_draggedSplitter
)); 
5191             state
->m_dontCenterSplitter 
= true; 
5193         // This is necessary to return cursor 
5194         if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
5197             m_iFlags 
&= ~(wxPG_FL_MOUSE_CAPTURED
); 
5200         // Set back the default cursor, if necessary 
5201         if ( splitterHit 
== -1 || 
5204             CustomSetCursor( wxCURSOR_ARROW 
); 
5209         // Control background needs to be cleared 
5210         wxPGProperty
* selected 
= GetSelection(); 
5211         if ( !(m_iFlags 
& wxPG_FL_PRIMARY_FILLS_ENTIRE
) && selected 
) 
5212             DrawItem( selected 
); 
5216             m_wndEditor
->Show ( true ); 
5219     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
5220         // Fixes button disappearance bug 
5222             m_wndEditor2
->Show ( true ); 
5225         // This clears the focus. 
5226         m_editorFocused 
= 0; 
5232 // ----------------------------------------------------------------------- 
5234 bool wxPropertyGrid::OnMouseCommon( wxMouseEvent
& event
, int* px
, int* py 
) 
5236     int splitterX 
= GetSplitterPosition(); 
5239     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &ux
, &uy 
); 
5241     wxWindow
* wnd 
= GetEditorControl(); 
5243     // Hide popup on clicks 
5244     if ( event
.GetEventType() != wxEVT_MOTION 
) 
5245         if ( wnd 
&& wnd
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)) ) 
5247             ((wxOwnerDrawnComboBox
*)wnd
)->HidePopup(); 
5253     if ( wnd 
== NULL 
|| m_dragStatus 
|| 
5255            ux 
<= (splitterX 
+ wxPG_SPLITTERX_DETECTMARGIN2
) || 
5256            ux 
>= (r
.x
+r
.width
) || 
5258            event
.m_y 
>= (r
.y
+r
.height
) 
5268         if ( m_curcursor 
!= wxCURSOR_ARROW 
) CustomSetCursor ( wxCURSOR_ARROW 
); 
5273 // ----------------------------------------------------------------------- 
5275 void wxPropertyGrid::OnMouseClick( wxMouseEvent 
&event 
) 
5278     if ( OnMouseCommon( event
, &x
, &y 
) ) 
5280         if ( !HandleMouseClick(x
, y
, event
) ) 
5289 // ----------------------------------------------------------------------- 
5291 void wxPropertyGrid::OnMouseRightClick( wxMouseEvent 
&event 
) 
5294     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &x
, &y 
); 
5295     HandleMouseRightClick(x
,y
,event
); 
5299 // ----------------------------------------------------------------------- 
5301 void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent 
&event 
) 
5303     // Always run standard mouse-down handler as well 
5304     OnMouseClick(event
); 
5307     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &x
, &y 
); 
5308     HandleMouseDoubleClick(x
,y
,event
); 
5310     // Do not Skip() event here - OnMouseClick() call above 
5311     // should have already taken care of it. 
5314 // ----------------------------------------------------------------------- 
5316 void wxPropertyGrid::OnMouseMove( wxMouseEvent 
&event 
) 
5319     if ( OnMouseCommon( event
, &x
, &y 
) ) 
5321         HandleMouseMove(x
,y
,event
); 
5326 // ----------------------------------------------------------------------- 
5328 void wxPropertyGrid::OnMouseUp( wxMouseEvent 
&event 
) 
5331     if ( OnMouseCommon( event
, &x
, &y 
) ) 
5333         if ( !HandleMouseUp(x
, y
, event
) ) 
5342 // ----------------------------------------------------------------------- 
5344 void wxPropertyGrid::OnMouseEntry( wxMouseEvent 
&event 
) 
5346     // This may get called from child control as well, so event's 
5347     // mouse position cannot be relied on. 
5349     if ( event
.Entering() ) 
5351         if ( !(m_iFlags 
& wxPG_FL_MOUSE_INSIDE
) ) 
5353             // TODO: Fix this (detect parent and only do 
5354             //   cursor trick if it is a manager). 
5355             wxASSERT( GetParent() ); 
5356             GetParent()->SetCursor(wxNullCursor
); 
5358             m_iFlags 
|= wxPG_FL_MOUSE_INSIDE
; 
5361             GetParent()->SetCursor(wxNullCursor
); 
5363     else if ( event
.Leaving() ) 
5365         // Without this, wxSpinCtrl editor will sometimes have wrong cursor 
5366         SetCursor( wxNullCursor 
); 
5368         // Get real cursor position 
5369         wxPoint pt 
= ScreenToClient(::wxGetMousePosition()); 
5371         if ( ( pt
.x 
<= 0 || pt
.y 
<= 0 || pt
.x 
>= m_width 
|| pt
.y 
>= m_height 
) ) 
5374                 if ( (m_iFlags 
& wxPG_FL_MOUSE_INSIDE
) ) 
5376                     m_iFlags 
&= ~(wxPG_FL_MOUSE_INSIDE
); 
5380                     wxPropertyGrid::HandleMouseUp ( -1, 10000, event 
); 
5388 // ----------------------------------------------------------------------- 
5390 // Common code used by various OnMouseXXXChild methods. 
5391 bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent 
&event
, int* px
, int *py 
) 
5393     wxWindow
* topCtrlWnd 
= (wxWindow
*)event
.GetEventObject(); 
5394     wxASSERT( topCtrlWnd 
); 
5396     event
.GetPosition(&x
,&y
); 
5398     int splitterX 
= GetSplitterPosition(); 
5400     wxRect r 
= topCtrlWnd
->GetRect(); 
5401     if ( !m_dragStatus 
&& 
5402          x 
> (splitterX
-r
.x
+wxPG_SPLITTERX_DETECTMARGIN2
) && 
5403          y 
>= 0 && y 
< r
.height \
 
5406         if ( m_curcursor 
!= wxCURSOR_ARROW 
) CustomSetCursor ( wxCURSOR_ARROW 
); 
5411         CalcUnscrolledPosition( event
.m_x 
+ r
.x
, event
.m_y 
+ r
.y
, \
 
5418 void wxPropertyGrid::OnMouseClickChild( wxMouseEvent 
&event 
) 
5421     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5423         bool res 
= HandleMouseClick(x
,y
,event
); 
5424         if ( !res 
) event
.Skip(); 
5428 void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent 
&event 
) 
5431     wxASSERT( m_wndEditor 
); 
5432     // These coords may not be exact (about +-2), 
5433     // but that should not matter (right click is about item, not position). 
5434     wxPoint pt 
= m_wndEditor
->GetPosition(); 
5435     CalcUnscrolledPosition( event
.m_x 
+ pt
.x
, event
.m_y 
+ pt
.y
, &x
, &y 
); 
5437     // FIXME: Used to set m_propHover to selection here. Was it really 
5440     bool res 
= HandleMouseRightClick(x
,y
,event
); 
5441     if ( !res 
) event
.Skip(); 
5444 void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent 
&event 
) 
5447     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5449         bool res 
= HandleMouseMove(x
,y
,event
); 
5450         if ( !res 
) event
.Skip(); 
5454 void wxPropertyGrid::OnMouseUpChild( wxMouseEvent 
&event 
) 
5457     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5459         bool res 
= HandleMouseUp(x
,y
,event
); 
5460         if ( !res 
) event
.Skip(); 
5464 // ----------------------------------------------------------------------- 
5465 // wxPropertyGrid keyboard event handling 
5466 // ----------------------------------------------------------------------- 
5468 int wxPropertyGrid::KeyEventToActions(wxKeyEvent 
&event
, int* pSecond
) const 
5470     // Translates wxKeyEvent to wxPG_ACTION_XXX 
5472     int keycode 
= event
.GetKeyCode(); 
5473     int modifiers 
= event
.GetModifiers(); 
5475     wxASSERT( !(modifiers
&~(0xFFFF)) ); 
5477     int hashMapKey 
= (keycode 
& 0xFFFF) | ((modifiers 
& 0xFFFF) << 16); 
5479     wxPGHashMapI2I::const_iterator it 
= m_actionTriggers
.find(hashMapKey
); 
5481     if ( it 
== m_actionTriggers
.end() ) 
5486         int second 
= (it
->second
>>16) & 0xFFFF; 
5490     return (it
->second 
& 0xFFFF); 
5493 void wxPropertyGrid::AddActionTrigger( int action
, int keycode
, int modifiers 
) 
5495     wxASSERT( !(modifiers
&~(0xFFFF)) ); 
5497     int hashMapKey 
= (keycode 
& 0xFFFF) | ((modifiers 
& 0xFFFF) << 16); 
5499     wxPGHashMapI2I::iterator it 
= m_actionTriggers
.find(hashMapKey
); 
5501     if ( it 
!= m_actionTriggers
.end() ) 
5503         // This key combination is already used 
5505         // Can add secondary? 
5506         wxASSERT_MSG( !(it
->second
&~(0xFFFF)), 
5507                       wxT("You can only add up to two separate actions per key combination.") ); 
5509         action 
= it
->second 
| (action
<<16); 
5512     m_actionTriggers
[hashMapKey
] = action
; 
5515 void wxPropertyGrid::ClearActionTriggers( int action 
) 
5517     wxPGHashMapI2I::iterator it
; 
5522         didSomething 
= false; 
5524         for ( it 
= m_actionTriggers
.begin(); 
5525               it 
!= m_actionTriggers
.end(); 
5528             if ( it
->second 
== action 
) 
5530                 m_actionTriggers
.erase(it
); 
5531                 didSomething 
= true; 
5536     while ( didSomething 
); 
5539 void wxPropertyGrid::HandleKeyEvent( wxKeyEvent 
&event
, bool fromChild 
) 
5542     // Handles key event when editor control is not focused. 
5545     wxCHECK2(!m_frozen
, return); 
5547     // Travelsal between items, collapsing/expanding, etc. 
5548     wxPGProperty
* selected 
= GetSelection(); 
5549     int keycode 
= event
.GetKeyCode(); 
5550     bool editorFocused 
= IsEditorFocused(); 
5552     if ( keycode 
== WXK_TAB 
) 
5554         wxWindow
* mainControl
; 
5556         if ( HasInternalFlag(wxPG_FL_IN_MANAGER
) ) 
5557             mainControl 
= GetParent(); 
5561         if ( !event
.ShiftDown() ) 
5563             if ( !editorFocused 
&& m_wndEditor 
) 
5565                 DoSelectProperty( selected
, wxPG_SEL_FOCUS 
); 
5569                 // Tab traversal workaround for platforms on which 
5570                 // wxWindow::Navigate() may navigate into first child 
5571                 // instead of next sibling. Does not work perfectly 
5572                 // in every scenario (for instance, when property grid 
5573                 // is either first or last control). 
5574             #if defined(__WXGTK__) 
5575                 wxWindow
* sibling 
= mainControl
->GetNextSibling(); 
5577                     sibling
->SetFocusFromKbd(); 
5579                 Navigate(wxNavigationKeyEvent::IsForward
); 
5585             if ( editorFocused 
) 
5591             #if defined(__WXGTK__) 
5592                 wxWindow
* sibling 
= mainControl
->GetPrevSibling(); 
5594                     sibling
->SetFocusFromKbd(); 
5596                 Navigate(wxNavigationKeyEvent::IsBackward
); 
5604     // Ignore Alt and Control when they are down alone 
5605     if ( keycode 
== WXK_ALT 
|| 
5606          keycode 
== WXK_CONTROL 
) 
5613     int action 
= KeyEventToActions(event
, &secondAction
); 
5615     if ( editorFocused 
&& action 
== wxPG_ACTION_CANCEL_EDIT 
) 
5618         // Esc cancels any changes 
5619         if ( IsEditorsValueModified() ) 
5621             EditorsValueWasNotModified(); 
5623             // Update the control as well 
5624             selected
->GetEditorClass()-> 
5625                 SetControlStringValue( selected
, 
5627                                        selected
->GetDisplayedString() ); 
5630         OnValidationFailureReset(selected
); 
5636     // Except for TAB, ESC, and any keys specifically dedicated to 
5637     // wxPropertyGrid itself, handle child control events in child control. 
5639          wxPGFindInVector(m_dedicatedKeys
, keycode
) == wxNOT_FOUND 
) 
5641         // Only propagate event if it had modifiers 
5642         if ( !event
.HasModifiers() ) 
5644             event
.StopPropagation(); 
5650     bool wasHandled 
= false; 
5655         if ( ButtonTriggerKeyTest(action
, event
) ) 
5658         wxPGProperty
* p 
= selected
; 
5660         // Travel and expand/collapse 
5663         if ( p
->GetChildCount() ) 
5665             if ( action 
== wxPG_ACTION_COLLAPSE_PROPERTY 
|| secondAction 
== wxPG_ACTION_COLLAPSE_PROPERTY 
) 
5667                 if ( (m_windowStyle 
& wxPG_HIDE_MARGIN
) || Collapse(p
) ) 
5670             else if ( action 
== wxPG_ACTION_EXPAND_PROPERTY 
|| secondAction 
== wxPG_ACTION_EXPAND_PROPERTY 
) 
5672                 if ( (m_windowStyle 
& wxPG_HIDE_MARGIN
) || Expand(p
) ) 
5679             if ( action 
== wxPG_ACTION_PREV_PROPERTY 
|| secondAction 
== wxPG_ACTION_PREV_PROPERTY 
) 
5683             else if ( action 
== wxPG_ACTION_NEXT_PROPERTY 
|| secondAction 
== wxPG_ACTION_NEXT_PROPERTY 
) 
5689         if ( selectDir 
>= -1 ) 
5691             p 
= wxPropertyGridIterator::OneStep( m_pState
, wxPG_ITERATE_VISIBLE
, p
, selectDir 
); 
5695                 int reopenLabelEditorCol 
= -1; 
5697                 if ( editorFocused 
) 
5699                     // If editor was focused, then make the next editor 
5701                     selFlags 
|= wxPG_SEL_FOCUS
; 
5705                     // Also maintain the same label editor focus state 
5706                     if ( m_labelEditor 
) 
5707                         reopenLabelEditorCol 
= m_selColumn
; 
5710                 DoSelectProperty(p
, selFlags
); 
5712                 if ( reopenLabelEditorCol 
>= 0 ) 
5713                     DoBeginLabelEdit(reopenLabelEditorCol
); 
5720         // If nothing was selected, select the first item now 
5721         // (or navigate out of tab). 
5722         if ( action 
!= wxPG_ACTION_CANCEL_EDIT 
&& secondAction 
!= wxPG_ACTION_CANCEL_EDIT 
) 
5724             wxPGProperty
* p 
= wxPropertyGridInterface::GetFirst(); 
5725             if ( p 
) DoSelectProperty(p
); 
5734 // ----------------------------------------------------------------------- 
5736 void wxPropertyGrid::OnKey( wxKeyEvent 
&event 
) 
5738     // If there was editor open and focused, then this event should not 
5739     // really be processed here. 
5740     if ( IsEditorFocused() ) 
5742         // However, if event had modifiers, it is probably still best 
5744         if ( event
.HasModifiers() ) 
5747             event
.StopPropagation(); 
5751     HandleKeyEvent(event
, false); 
5754 // ----------------------------------------------------------------------- 
5756 bool wxPropertyGrid::ButtonTriggerKeyTest( int action
, wxKeyEvent
& event 
) 
5761         action 
= KeyEventToActions(event
, &secondAction
); 
5764     // Does the keycode trigger button? 
5765     if ( action 
== wxPG_ACTION_PRESS_BUTTON 
&& 
5768         wxCommandEvent 
evt(wxEVT_COMMAND_BUTTON_CLICKED
, m_wndEditor2
->GetId()); 
5769         GetEventHandler()->AddPendingEvent(evt
); 
5776 // ----------------------------------------------------------------------- 
5778 void wxPropertyGrid::OnChildKeyDown( wxKeyEvent 
&event 
) 
5780     HandleKeyEvent(event
, true); 
5783 // ----------------------------------------------------------------------- 
5784 // wxPropertyGrid miscellaneous event handling 
5785 // ----------------------------------------------------------------------- 
5787 void wxPropertyGrid::OnIdle( wxIdleEvent
& WXUNUSED(event
) ) 
5790     // Check if the focus is in this control or one of its children 
5791     wxWindow
* newFocused 
= wxWindow::FindFocus(); 
5793     if ( newFocused 
!= m_curFocused 
) 
5794         HandleFocusChange( newFocused 
); 
5797     // Check if top-level parent has changed 
5798     if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING 
) 
5800         wxWindow
* tlp 
= ::wxGetTopLevelParent(this); 
5806     // Resolve pending property removals 
5807     if ( m_deletedProperties
.size() > 0 ) 
5809         wxArrayPGProperty
& arr 
= m_deletedProperties
; 
5810         for ( unsigned int i
=0; i
<arr
.size(); i
++ ) 
5812             DeleteProperty(arr
[i
]); 
5816     if ( m_removedProperties
.size() > 0 ) 
5818         wxArrayPGProperty
& arr 
= m_removedProperties
; 
5819         for ( unsigned int i
=0; i
<arr
.size(); i
++ ) 
5821             RemoveProperty(arr
[i
]); 
5827 bool wxPropertyGrid::IsEditorFocused() const 
5829     wxWindow
* focus 
= wxWindow::FindFocus(); 
5831     if ( focus 
== m_wndEditor 
|| focus 
== m_wndEditor2 
|| 
5832          focus 
== GetEditorControl() ) 
5838 // Called by focus event handlers. newFocused is the window that becomes focused. 
5839 void wxPropertyGrid::HandleFocusChange( wxWindow
* newFocused 
) 
5842     // Never allow focus to be changed when handling editor event. 
5843     // Especially because they may be displaing a dialog which 
5844     // could cause all kinds of weird (native) focus changes. 
5845     if ( HasInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
) ) 
5848     unsigned int oldFlags 
= m_iFlags
; 
5849     bool wasEditorFocused 
= false; 
5850     wxWindow
* wndEditor 
= m_wndEditor
; 
5852     m_iFlags 
&= ~(wxPG_FL_FOCUSED
); 
5854     wxWindow
* parent 
= newFocused
; 
5856     // This must be one of nextFocus' parents. 
5859         if ( parent 
== wndEditor 
) 
5861             wasEditorFocused 
= true; 
5863         // Use m_eventObject, which is either wxPropertyGrid or 
5864         // wxPropertyGridManager, as appropriate. 
5865         else if ( parent 
== m_eventObject 
) 
5867             m_iFlags 
|= wxPG_FL_FOCUSED
; 
5870         parent 
= parent
->GetParent(); 
5873     // Notify editor control when it receives a focus 
5874     if ( wasEditorFocused 
&& m_curFocused 
!= newFocused 
) 
5876         wxPGProperty
* p 
= GetSelection(); 
5879             const wxPGEditor
* editor 
= p
->GetEditorClass(); 
5880             ResetEditorAppearance(); 
5881             editor
->OnFocus(p
, GetEditorControl()); 
5885     m_curFocused 
= newFocused
; 
5887     if ( (m_iFlags 
& wxPG_FL_FOCUSED
) != 
5888          (oldFlags 
& wxPG_FL_FOCUSED
) ) 
5890         if ( !(m_iFlags 
& wxPG_FL_FOCUSED
) ) 
5892             // Need to store changed value 
5893             CommitChangesFromEditor(); 
5899             // Preliminary code for tab-order respecting 
5900             // tab-traversal (but should be moved to 
5903             wxWindow* prevFocus = event.GetWindow(); 
5904             wxWindow* useThis = this; 
5905             if ( m_iFlags & wxPG_FL_IN_MANAGER ) 
5906                 useThis = GetParent(); 
5909                  prevFocus->GetParent() == useThis->GetParent() ) 
5911                 wxList& children = useThis->GetParent()->GetChildren(); 
5913                 wxNode* node = children.Find(prevFocus); 
5915                 if ( node->GetNext() && 
5916                      useThis == node->GetNext()->GetData() ) 
5917                     DoSelectProperty(GetFirst()); 
5918                 else if ( node->GetPrevious () && 
5919                           useThis == node->GetPrevious()->GetData() ) 
5920                     DoSelectProperty(GetLastProperty()); 
5927         wxPGProperty
* selected 
= GetSelection(); 
5928         if ( selected 
&& (m_iFlags 
& wxPG_FL_INITIALIZED
) ) 
5929             DrawItem( selected 
); 
5933 void wxPropertyGrid::OnFocusEvent( wxFocusEvent
& event 
) 
5935     if ( event
.GetEventType() == wxEVT_SET_FOCUS 
) 
5936         HandleFocusChange((wxWindow
*)event
.GetEventObject()); 
5937     // Line changed to "else" when applying wxPropertyGrid patch #1675902 
5938     //else if ( event.GetWindow() ) 
5940         HandleFocusChange(event
.GetWindow()); 
5945 // ----------------------------------------------------------------------- 
5947 void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent
& event 
) 
5949     HandleFocusChange((wxWindow
*)event
.GetEventObject()); 
5953 // ----------------------------------------------------------------------- 
5955 void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent 
&event 
) 
5957     m_iFlags 
|= wxPG_FL_SCROLLED
; 
5962 // ----------------------------------------------------------------------- 
5964 void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent
& WXUNUSED(event
) ) 
5966     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
5968         m_iFlags 
&= ~(wxPG_FL_MOUSE_CAPTURED
); 
5972 // ----------------------------------------------------------------------- 
5973 // Property editor related functions 
5974 // ----------------------------------------------------------------------- 
5976 // noDefCheck = true prevents infinite recursion. 
5977 wxPGEditor
* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor
* editorClass
, 
5978                                                    const wxString
& editorName
, 
5981     wxASSERT( editorClass 
); 
5983     if ( !noDefCheck 
&& wxPGGlobalVars
->m_mapEditorClasses
.empty() ) 
5984         RegisterDefaultEditors(); 
5986     wxString name 
= editorName
; 
5987     if ( name
.length() == 0 ) 
5988         name 
= editorClass
->GetName(); 
5990     // Existing editor under this name? 
5991     wxPGHashMapS2P::iterator vt_it 
= wxPGGlobalVars
->m_mapEditorClasses
.find(name
); 
5993     if ( vt_it 
!= wxPGGlobalVars
->m_mapEditorClasses
.end() ) 
5995         // If this name was already used, try class name. 
5996         name 
= editorClass
->GetClassInfo()->GetClassName(); 
5997         vt_it 
= wxPGGlobalVars
->m_mapEditorClasses
.find(name
); 
6000     wxCHECK_MSG( vt_it 
== wxPGGlobalVars
->m_mapEditorClasses
.end(), 
6001                  (wxPGEditor
*) vt_it
->second
, 
6002                  "Editor with given name was already registered" ); 
6004     wxPGGlobalVars
->m_mapEditorClasses
[name
] = (void*)editorClass
; 
6009 // Use this in RegisterDefaultEditors. 
6010 #define wxPGRegisterDefaultEditorClass(EDITOR) \ 
6011     if ( wxPGEditor_##EDITOR == NULL ) \ 
6013         wxPGEditor_##EDITOR = wxPropertyGrid::RegisterEditorClass( \ 
6014             new wxPG##EDITOR##Editor, true ); \ 
6017 // Registers all default editor classes 
6018 void wxPropertyGrid::RegisterDefaultEditors() 
6020     wxPGRegisterDefaultEditorClass( TextCtrl 
); 
6021     wxPGRegisterDefaultEditorClass( Choice 
); 
6022     wxPGRegisterDefaultEditorClass( ComboBox 
); 
6023     wxPGRegisterDefaultEditorClass( TextCtrlAndButton 
); 
6024 #if wxPG_INCLUDE_CHECKBOX 
6025     wxPGRegisterDefaultEditorClass( CheckBox 
); 
6027     wxPGRegisterDefaultEditorClass( ChoiceAndButton 
); 
6029     // Register SpinCtrl etc. editors before use 
6030     RegisterAdditionalEditors(); 
6033 // ----------------------------------------------------------------------- 
6034 // wxPGStringTokenizer 
6035 //   Needed to handle C-style string lists (e.g. "str1" "str2") 
6036 // ----------------------------------------------------------------------- 
6038 wxPGStringTokenizer::wxPGStringTokenizer( const wxString
& str
, wxChar delimeter 
) 
6039     : m_str(&str
), m_curPos(str
.begin()), m_delimeter(delimeter
) 
6043 wxPGStringTokenizer::~wxPGStringTokenizer() 
6047 bool wxPGStringTokenizer::HasMoreTokens() 
6049     const wxString
& str 
= *m_str
; 
6051     wxString::const_iterator i 
= m_curPos
; 
6053     wxUniChar delim 
= m_delimeter
; 
6055     wxUniChar prev_a 
= wxT('\0'); 
6057     bool inToken 
= false; 
6059     while ( i 
!= str
.end() ) 
6068                 m_readyToken
.clear(); 
6073             if ( prev_a 
!= wxT('\\') ) 
6077                     if ( a 
!= wxT('\\') ) 
6097     m_curPos 
= str
.end(); 
6105 wxString 
wxPGStringTokenizer::GetNextToken() 
6107     return m_readyToken
; 
6110 // ----------------------------------------------------------------------- 
6112 // ----------------------------------------------------------------------- 
6114 wxPGChoiceEntry::wxPGChoiceEntry() 
6115     : wxPGCell(), m_value(wxPG_INVALID_VALUE
) 
6119 // ----------------------------------------------------------------------- 
6121 // ----------------------------------------------------------------------- 
6123 wxPGChoicesData::wxPGChoicesData() 
6127 wxPGChoicesData::~wxPGChoicesData() 
6132 void wxPGChoicesData::Clear() 
6137 void wxPGChoicesData::CopyDataFrom( wxPGChoicesData
* data 
) 
6139     wxASSERT( m_items
.size() == 0 ); 
6141     m_items 
= data
->m_items
; 
6144 wxPGChoiceEntry
& wxPGChoicesData::Insert( int index
, 
6145                                           const wxPGChoiceEntry
& item 
) 
6147     wxVector
<wxPGChoiceEntry
>::iterator it
; 
6151         index 
= (int) m_items
.size(); 
6155         it 
= m_items
.begin() + index
; 
6158     m_items
.insert(it
, item
); 
6160     wxPGChoiceEntry
& ownEntry 
= m_items
[index
]; 
6162     // Need to fix value? 
6163     if ( ownEntry
.GetValue() == wxPG_INVALID_VALUE 
) 
6164         ownEntry
.SetValue(index
); 
6169 // ----------------------------------------------------------------------- 
6170 // wxPropertyGridEvent 
6171 // ----------------------------------------------------------------------- 
6173 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent
, wxCommandEvent
) 
6176 wxDEFINE_EVENT( wxEVT_PG_SELECTED
, wxPropertyGridEvent 
); 
6177 wxDEFINE_EVENT( wxEVT_PG_CHANGING
, wxPropertyGridEvent 
); 
6178 wxDEFINE_EVENT( wxEVT_PG_CHANGED
, wxPropertyGridEvent 
); 
6179 wxDEFINE_EVENT( wxEVT_PG_HIGHLIGHTED
, wxPropertyGridEvent 
); 
6180 wxDEFINE_EVENT( wxEVT_PG_RIGHT_CLICK
, wxPropertyGridEvent 
); 
6181 wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED
, wxPropertyGridEvent 
); 
6182 wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED
, wxPropertyGridEvent 
); 
6183 wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED
, wxPropertyGridEvent 
); 
6184 wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK
, wxPropertyGridEvent 
); 
6185 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN
, wxPropertyGridEvent 
); 
6186 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING
, wxPropertyGridEvent 
); 
6187 wxDEFINE_EVENT( wxEVT_PG_COL_BEGIN_DRAG
, wxPropertyGridEvent 
); 
6188 wxDEFINE_EVENT( wxEVT_PG_COL_DRAGGING
, wxPropertyGridEvent 
); 
6189 wxDEFINE_EVENT( wxEVT_PG_COL_END_DRAG
, wxPropertyGridEvent 
); 
6191 // ----------------------------------------------------------------------- 
6193 void wxPropertyGridEvent::Init() 
6195     m_validationInfo 
= NULL
; 
6198     m_wasVetoed 
= false; 
6201 // ----------------------------------------------------------------------- 
6203 wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType
, int id
) 
6204     : wxCommandEvent(commandType
,id
) 
6210 // ----------------------------------------------------------------------- 
6212 wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent
& event
) 
6213     : wxCommandEvent(event
) 
6215     m_eventType 
= event
.GetEventType(); 
6216     m_eventObject 
= event
.m_eventObject
; 
6218     OnPropertyGridSet(); 
6219     m_property 
= event
.m_property
; 
6220     m_validationInfo 
= event
.m_validationInfo
; 
6221     m_canVeto 
= event
.m_canVeto
; 
6222     m_wasVetoed 
= event
.m_wasVetoed
; 
6225 // ----------------------------------------------------------------------- 
6227 void wxPropertyGridEvent::OnPropertyGridSet() 
6233     wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
6235     m_pg
->m_liveEvents
.push_back(this); 
6238 // ----------------------------------------------------------------------- 
6240 wxPropertyGridEvent::~wxPropertyGridEvent() 
6245         wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
6248         // Use iterate from the back since it is more likely that the event 
6249         // being desroyed is at the end of the array. 
6250         wxVector
<wxPropertyGridEvent
*>& liveEvents 
= m_pg
->m_liveEvents
; 
6252         for ( int i 
= liveEvents
.size()-1; i 
>= 0; i
-- ) 
6254             if ( liveEvents
[i
] == this ) 
6256                 liveEvents
.erase(liveEvents
.begin() + i
); 
6263 // ----------------------------------------------------------------------- 
6265 wxEvent
* wxPropertyGridEvent::Clone() const 
6267     return new wxPropertyGridEvent( *this ); 
6270 // ----------------------------------------------------------------------- 
6271 // wxPropertyGridPopulator 
6272 // ----------------------------------------------------------------------- 
6274 wxPropertyGridPopulator::wxPropertyGridPopulator() 
6278     wxPGGlobalVars
->m_offline
++; 
6281 // ----------------------------------------------------------------------- 
6283 void wxPropertyGridPopulator::SetState( wxPropertyGridPageState
* state 
) 
6286     m_propHierarchy
.clear(); 
6289 // ----------------------------------------------------------------------- 
6291 void wxPropertyGridPopulator::SetGrid( wxPropertyGrid
* pg 
) 
6297 // ----------------------------------------------------------------------- 
6299 wxPropertyGridPopulator::~wxPropertyGridPopulator() 
6302     // Free unused sets of choices 
6303     wxPGHashMapS2P::iterator it
; 
6305     for( it 
= m_dictIdChoices
.begin(); it 
!= m_dictIdChoices
.end(); ++it 
) 
6307         wxPGChoicesData
* data 
= (wxPGChoicesData
*) it
->second
; 
6314         m_pg
->GetPanel()->Refresh(); 
6316     wxPGGlobalVars
->m_offline
--; 
6319 // ----------------------------------------------------------------------- 
6321 wxPGProperty
* wxPropertyGridPopulator::Add( const wxString
& propClass
, 
6322                                             const wxString
& propLabel
, 
6323                                             const wxString
& propName
, 
6324                                             const wxString
* propValue
, 
6325                                             wxPGChoices
* pChoices 
) 
6327     wxClassInfo
* classInfo 
= wxClassInfo::FindClass(propClass
); 
6328     wxPGProperty
* parent 
= GetCurParent(); 
6330     if ( parent
->HasFlag(wxPG_PROP_AGGREGATE
) ) 
6332         ProcessError(wxString::Format(wxT("new children cannot be added to '%s'"),parent
->GetName().c_str())); 
6336     if ( !classInfo 
|| !classInfo
->IsKindOf(CLASSINFO(wxPGProperty
)) ) 
6338         ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass
.c_str())); 
6342     wxPGProperty
* property 
= (wxPGProperty
*) classInfo
->CreateObject(); 
6344     property
->SetLabel(propLabel
); 
6345     property
->DoSetName(propName
); 
6347     if ( pChoices 
&& pChoices
->IsOk() ) 
6348         property
->SetChoices(*pChoices
); 
6350     m_state
->DoInsert(parent
, -1, property
); 
6353         property
->SetValueFromString( *propValue
, wxPG_FULL_VALUE
| 
6354                                                   wxPG_PROGRAMMATIC_VALUE 
); 
6359 // ----------------------------------------------------------------------- 
6361 void wxPropertyGridPopulator::AddChildren( wxPGProperty
* property 
) 
6363     m_propHierarchy
.push_back(property
); 
6364     DoScanForChildren(); 
6365     m_propHierarchy
.pop_back(); 
6368 // ----------------------------------------------------------------------- 
6370 wxPGChoices 
wxPropertyGridPopulator::ParseChoices( const wxString
& choicesString
, 
6371                                                    const wxString
& idString 
) 
6373     wxPGChoices choices
; 
6376     if ( choicesString
[0] == wxT('@') ) 
6378         wxString ids 
= choicesString
.substr(1); 
6379         wxPGHashMapS2P::iterator it 
= m_dictIdChoices
.find(ids
); 
6380         if ( it 
== m_dictIdChoices
.end() ) 
6381             ProcessError(wxString::Format(wxT("No choices defined for id '%s'"),ids
.c_str())); 
6383             choices
.AssignData((wxPGChoicesData
*)it
->second
); 
6388         if ( idString
.length() ) 
6390             wxPGHashMapS2P::iterator it 
= m_dictIdChoices
.find(idString
); 
6391             if ( it 
!= m_dictIdChoices
.end() ) 
6393                 choices
.AssignData((wxPGChoicesData
*)it
->second
); 
6400             // Parse choices string 
6401             wxString::const_iterator it 
= choicesString
.begin(); 
6405             bool labelValid 
= false; 
6407             for ( ; it 
!= choicesString
.end(); ++it 
) 
6413                     if ( c 
== wxT('"') ) 
6418                             if ( !value
.ToLong(&l
, 0) ) l 
= wxPG_INVALID_VALUE
; 
6419                             choices
.Add(label
, l
); 
6422                         //wxLogDebug(wxT("%s, %s"),label.c_str(),value.c_str()); 
6427                     else if ( c 
== wxT('=') ) 
6434                     else if ( state 
== 2 && (wxIsalnum(c
) || c 
== wxT('x')) ) 
6441                     if ( c 
== wxT('"') ) 
6454                 if ( !value
.ToLong(&l
, 0) ) l 
= wxPG_INVALID_VALUE
; 
6455                 choices
.Add(label
, l
); 
6458             if ( !choices
.IsOk() ) 
6460                 choices
.EnsureData(); 
6464             if ( idString
.length() ) 
6465                 m_dictIdChoices
[idString
] = choices
.GetData(); 
6472 // ----------------------------------------------------------------------- 
6474 bool wxPropertyGridPopulator::ToLongPCT( const wxString
& s
, long* pval
, long max 
) 
6476     if ( s
.Last() == wxT('%') ) 
6478         wxString s2 
= s
.substr(0,s
.length()-1); 
6480         if ( s2
.ToLong(&val
, 10) ) 
6482             *pval 
= (val
*max
)/100; 
6488     return s
.ToLong(pval
, 10); 
6491 // ----------------------------------------------------------------------- 
6493 bool wxPropertyGridPopulator::AddAttribute( const wxString
& name
, 
6494                                             const wxString
& type
, 
6495                                             const wxString
& value 
) 
6497     int l 
= m_propHierarchy
.size(); 
6501     wxPGProperty
* p 
= m_propHierarchy
[l
-1]; 
6502     wxString valuel 
= value
.Lower(); 
6505     if ( type
.length() == 0 ) 
6510         if ( valuel 
== wxT("true") || valuel 
== wxT("yes") || valuel 
== wxT("1") ) 
6512         else if ( valuel 
== wxT("false") || valuel 
== wxT("no") || valuel 
== wxT("0") ) 
6514         else if ( value
.ToLong(&v
, 0) ) 
6521         if ( type 
== wxT("string") ) 
6525         else if ( type 
== wxT("int") ) 
6528             value
.ToLong(&v
, 0); 
6531         else if ( type 
== wxT("bool") ) 
6533             if ( valuel 
== wxT("true") || valuel 
== wxT("yes") || valuel 
== wxT("1") ) 
6540             ProcessError(wxString::Format(wxT("Invalid attribute type '%s'"),type
.c_str())); 
6545     p
->SetAttribute( name
, variant 
); 
6550 // ----------------------------------------------------------------------- 
6552 void wxPropertyGridPopulator::ProcessError( const wxString
& msg 
) 
6554     wxLogError(_("Error in resource: %s"),msg
.c_str()); 
6557 // ----------------------------------------------------------------------- 
6559 #endif  // wxUSE_PROPGRID