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     // Make sure the global pointers have been reset 
 239     wxASSERT(wxPG_EDITOR(TextCtrl
) == NULL
); 
 240     wxASSERT(wxPG_EDITOR(ChoiceAndButton
) == NULL
); 
 242     delete wxPGProperty::sm_wxPG_LABEL
; 
 245 void wxPropertyGridInitGlobalsIfNeeded() 
 249 // ----------------------------------------------------------------------- 
 251 // ----------------------------------------------------------------------- 
 253 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid
, wxControl
) 
 255 BEGIN_EVENT_TABLE(wxPropertyGrid
, wxControl
) 
 256   EVT_IDLE(wxPropertyGrid::OnIdle
) 
 257   EVT_PAINT(wxPropertyGrid::OnPaint
) 
 258   EVT_SIZE(wxPropertyGrid::OnResize
) 
 259   EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry
) 
 260   EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry
) 
 261   EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange
) 
 262   EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent
) 
 263   EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent
) 
 264   EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent
) 
 265   EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent
) 
 266   EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged
) 
 267   EVT_MOTION(wxPropertyGrid::OnMouseMove
) 
 268   EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick
) 
 269   EVT_LEFT_UP(wxPropertyGrid::OnMouseUp
) 
 270   EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick
) 
 271   EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick
) 
 272   EVT_KEY_DOWN(wxPropertyGrid::OnKey
) 
 275 // ----------------------------------------------------------------------- 
 277 wxPropertyGrid::wxPropertyGrid() 
 278     : wxControl(), wxScrollHelper(this) 
 283 // ----------------------------------------------------------------------- 
 285 wxPropertyGrid::wxPropertyGrid( wxWindow 
*parent
, 
 290                                 const wxString
& name 
) 
 291     : wxControl(), wxScrollHelper(this) 
 294     Create(parent
,id
,pos
,size
,style
,name
); 
 297 // ----------------------------------------------------------------------- 
 299 bool wxPropertyGrid::Create( wxWindow 
*parent
, 
 304                              const wxString
& name 
) 
 307     if (!(style
&wxBORDER_MASK
)) 
 309         style 
|= wxBORDER_THEME
; 
 314     // Filter out wxTAB_TRAVERSAL - we will handle TABs manually 
 315     style 
&= ~(wxTAB_TRAVERSAL
); 
 316     style 
|= wxWANTS_CHARS
; 
 318     wxControl::Create(parent
, id
, pos
, size
, 
 319                       style 
| wxScrolledWindowStyle
, 
 328 // ----------------------------------------------------------------------- 
 331 // Initialize values to defaults 
 333 void wxPropertyGrid::Init1() 
 335     // Register editor classes, if necessary. 
 336     if ( wxPGGlobalVars
->m_mapEditorClasses
.empty() ) 
 337         wxPropertyGrid::RegisterDefaultEditors(); 
 339     m_validatingEditor 
= 0; 
 342     m_wndEditor 
= m_wndEditor2 
= NULL
; 
 346     m_labelEditor 
= NULL
; 
 347     m_labelEditorProperty 
= NULL
; 
 348     m_eventObject 
= this; 
 350     m_processedEvent 
= NULL
; 
 352     m_sortFunction 
= NULL
; 
 353     m_inDoPropertyChanged 
= false; 
 354     m_inCommitChangesFromEditor 
= false; 
 355     m_inDoSelectProperty 
= false; 
 356     m_inOnValidationFailure 
= false; 
 357     m_permanentValidationFailureBehavior 
= wxPG_VFB_DEFAULT
; 
 362     // Set up default unspecified value 'colour' 
 363     m_unspecifiedAppearance
.SetFgCol(*wxLIGHT_GREY
); 
 366     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY
, WXK_RIGHT 
); 
 367     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY
, WXK_DOWN 
); 
 368     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY
, WXK_LEFT 
); 
 369     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY
, WXK_UP 
); 
 370     AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY
, WXK_RIGHT
); 
 371     AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY
, WXK_LEFT
); 
 372     AddActionTrigger( wxPG_ACTION_CANCEL_EDIT
, WXK_ESCAPE 
); 
 373     AddActionTrigger( wxPG_ACTION_PRESS_BUTTON
, WXK_DOWN
, wxMOD_ALT 
); 
 374     AddActionTrigger( wxPG_ACTION_PRESS_BUTTON
, WXK_F4 
); 
 376     m_coloursCustomized 
= 0; 
 379     m_doubleBuffer 
= NULL
; 
 381 #ifndef wxPG_ICON_WIDTH 
 387     m_iconWidth 
= wxPG_ICON_WIDTH
; 
 392     m_gutterWidth 
= wxPG_GUTTER_MIN
; 
 393     m_subgroup_extramargin 
= 10; 
 397     m_width 
= m_height 
= 0; 
 399     m_commonValues
.push_back(new wxPGCommonValue(_("Unspecified"), wxPGGlobalVars
->m_defaultRenderer
) ); 
 402     m_chgInfo_changedProperty 
= NULL
; 
 405 // ----------------------------------------------------------------------- 
 408 // Initialize after parent etc. set 
 410 void wxPropertyGrid::Init2() 
 412     wxASSERT( !(m_iFlags 
& wxPG_FL_INITIALIZED 
) ); 
 415    // Smaller controls on Mac 
 416    SetWindowVariant(wxWINDOW_VARIANT_SMALL
); 
 419     // Now create state, if one didn't exist already 
 420     // (wxPropertyGridManager might have created it for us). 
 423         m_pState 
= CreateState(); 
 424         m_pState
->m_pPropGrid 
= this; 
 425         m_iFlags 
|= wxPG_FL_CREATEDSTATE
; 
 428     if ( !(m_windowStyle 
& wxPG_SPLITTER_AUTO_CENTER
) ) 
 429         m_pState
->m_dontCenterSplitter 
= true; 
 431     if ( m_windowStyle 
& wxPG_HIDE_CATEGORIES 
) 
 433         m_pState
->InitNonCatMode(); 
 435         m_pState
->m_properties 
= m_pState
->m_abcArray
; 
 438     GetClientSize(&m_width
,&m_height
); 
 440 #ifndef wxPG_ICON_WIDTH 
 441     // create two bitmap nodes for drawing 
 442     m_expandbmp 
= new wxBitmap(expand_xpm
); 
 443     m_collbmp 
= new wxBitmap(collapse_xpm
); 
 445     // calculate average font height for bitmap centering 
 447     m_iconWidth 
= m_expandbmp
->GetWidth(); 
 448     m_iconHeight 
= m_expandbmp
->GetHeight(); 
 451     m_curcursor 
= wxCURSOR_ARROW
; 
 452     m_cursorSizeWE 
= new wxCursor( wxCURSOR_SIZEWE 
); 
 454     // adjust bitmap icon y position so they are centered 
 455     m_vspacing 
= wxPG_DEFAULT_VSPACING
; 
 457     CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING 
); 
 459     // Allocate cell datas 
 460     m_propertyDefaultCell
.SetEmptyData(); 
 461     m_categoryDefaultCell
.SetEmptyData(); 
 465     // This helps with flicker 
 466     SetBackgroundStyle( wxBG_STYLE_CUSTOM 
); 
 468     // Hook the top-level parent 
 472     // set virtual size to this window size 
 473     wxSize wndsize 
= GetSize(); 
 474     SetVirtualSize(wndsize
.GetWidth(), wndsize
.GetWidth()); 
 476     m_timeCreated 
= ::wxGetLocalTimeMillis(); 
 478     m_iFlags 
|= wxPG_FL_INITIALIZED
; 
 480     m_ncWidth 
= wndsize
.GetWidth(); 
 482     // Need to call OnResize handler or size given in constructor/Create 
 484     wxSizeEvent 
sizeEvent(wndsize
,0); 
 488 // ----------------------------------------------------------------------- 
 490 wxPropertyGrid::~wxPropertyGrid() 
 495     wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
 499     // Remove grid and property pointers from live wxPropertyGridEvents. 
 500     for ( i
=0; i
<m_liveEvents
.size(); i
++ ) 
 502         wxPropertyGridEvent
* evt 
= m_liveEvents
[i
]; 
 503         evt
->SetPropertyGrid(NULL
); 
 504         evt
->SetProperty(NULL
); 
 506     m_liveEvents
.clear(); 
 508     if ( m_processedEvent 
) 
 510         // All right... we are being deleted while wxPropertyGrid event 
 511         // is being sent. Make sure that event propagates as little 
 512         // as possible (although usually this is not enough to prevent 
 514         m_processedEvent
->Skip(false); 
 515         m_processedEvent
->StopPropagation(); 
 517         // Let's use wxMessageBox to make the message appear more 
 518         // reliably (and *before* the crash can happen). 
 519         ::wxMessageBox("wxPropertyGrid was being destroyed in an event " 
 520                        "generated by it. This usually leads to a crash " 
 521                        "so it is recommended to destroy the control " 
 522                        "at idle time instead."); 
 525     DoSelectProperty(NULL
, wxPG_SEL_NOVALIDATE
|wxPG_SEL_DONT_SEND_EVENT
); 
 527     // This should do prevent things from going too badly wrong 
 528     m_iFlags 
&= ~(wxPG_FL_INITIALIZED
); 
 530     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
 533     // Call with NULL to disconnect event handling 
 534     if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING 
) 
 538         wxASSERT_MSG( !IsEditorsValueModified(), 
 539                       wxS("Most recent change in property editor was ") 
 540                       wxS("lost!!! (if you don't want this to happen, ") 
 541                       wxS("close your frames and dialogs using ") 
 542                       wxS("Close(false).)") ); 
 545     if ( m_doubleBuffer 
) 
 546         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(); 
 673         // Force property re-selection 
 674         // NB: We must copy the selection. 
 675         wxArrayPGProperty selection 
= m_pState
->m_selection
; 
 676         DoSetSelection(selection
, wxPG_SEL_FORCE 
| wxPG_SEL_NONVISIBLE
); 
 680 // ----------------------------------------------------------------------- 
 682 bool wxPropertyGrid::DoAddToSelection( wxPGProperty
* prop
, int selFlags 
) 
 684     wxCHECK( prop
, false ); 
 686     if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION
) ) 
 687         return DoSelectProperty(prop
, selFlags
); 
 689     wxArrayPGProperty
& selection 
= m_pState
->m_selection
; 
 691     if ( !selection
.size() ) 
 693         return DoSelectProperty(prop
, selFlags
); 
 697         // For categories, only one can be selected at a time 
 698         if ( prop
->IsCategory() || selection
[0]->IsCategory() ) 
 701         selection
.push_back(prop
); 
 703         if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 705             SendEvent( wxEVT_PG_SELECTED
, prop
, NULL 
); 
 714 // ----------------------------------------------------------------------- 
 716 bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty
* prop
, int selFlags 
) 
 718     wxCHECK( prop
, false ); 
 721     wxArrayPGProperty
& selection 
= m_pState
->m_selection
; 
 722     if ( selection
.size() <= 1 ) 
 724         res 
= DoSelectProperty(NULL
, selFlags
); 
 728         m_pState
->DoRemoveFromSelection(prop
); 
 736 // ----------------------------------------------------------------------- 
 738 bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty
* prop
, 
 739                                       unsigned int colIndex
, 
 740                                       unsigned int selFlags 
) 
 743     // NB: Enable following if label editor background colour is 
 744     //     ever changed to any other than m_colSelBack. 
 746     // We use this workaround to prevent visible flicker when editing 
 747     // a cell. Atleast on wxMSW, there is a difficult to find 
 748     // (and perhaps prevent) redraw somewhere between making property 
 749     // selected and enabling label editing. 
 751     //wxColour prevColSelBack = m_colSelBack; 
 752     //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); 
 758         res 
= DoSelectProperty(prop
, selFlags
); 
 763         DoClearSelection(false, wxPG_SEL_NO_REFRESH
); 
 765         if ( m_pState
->m_editableColumns
.Index(colIndex
) == wxNOT_FOUND 
) 
 767             res 
= DoAddToSelection(prop
, selFlags
); 
 771             res 
= DoAddToSelection(prop
, selFlags
|wxPG_SEL_NO_REFRESH
); 
 773             DoBeginLabelEdit(colIndex
, selFlags
); 
 777     //m_colSelBack = prevColSelBack; 
 781 // ----------------------------------------------------------------------- 
 783 bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty
* prop
, 
 784                                                    unsigned int colIndex
, 
 785                                                    wxMouseEvent
* mouseEvent
, 
 788     const wxArrayPGProperty
& selection 
= GetSelectedProperties(); 
 789     bool alreadySelected 
= m_pState
->DoIsPropertySelected(prop
); 
 792     // Set to 2 if also add all items in between 
 793     int addToExistingSelection 
= 0; 
 795     if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION 
) 
 799             if ( mouseEvent
->GetEventType() == wxEVT_RIGHT_DOWN 
|| 
 800                  mouseEvent
->GetEventType() == wxEVT_RIGHT_UP 
) 
 802                 // Allow right-click for context menu without 
 803                 // disturbing the selection. 
 804                 if ( GetSelectedProperties().size() <= 1 || 
 806                     return DoSelectAndEdit(prop
, colIndex
, selFlags
); 
 811                 if ( mouseEvent
->ControlDown() ) 
 813                     addToExistingSelection 
= 1; 
 815                 else if ( mouseEvent
->ShiftDown() ) 
 817                     if ( selection
.size() > 0 && !prop
->IsCategory() ) 
 818                         addToExistingSelection 
= 2; 
 820                         addToExistingSelection 
= 1; 
 826     if ( addToExistingSelection 
== 1 ) 
 829         if ( !alreadySelected 
) 
 831             res 
= DoAddToSelection(prop
, selFlags
); 
 833         else if ( GetSelectedProperties().size() > 1 ) 
 835             res 
= DoRemoveFromSelection(prop
, selFlags
); 
 838     else if ( addToExistingSelection 
== 2 ) 
 840         // Add this, and all in between 
 842         // Find top selected property 
 843         wxPGProperty
* topSelProp 
= selection
[0]; 
 844         int topSelPropY 
= topSelProp
->GetY(); 
 845         for ( unsigned int i
=1; i
<selection
.size(); i
++ ) 
 847             wxPGProperty
* p 
= selection
[i
]; 
 849             if ( y 
< topSelPropY 
) 
 856         wxPGProperty
* startFrom
; 
 857         wxPGProperty
* stopAt
; 
 859         if ( prop
->GetY() <= topSelPropY 
) 
 861             // Property is above selection (or same) 
 867             // Property is below selection 
 868             startFrom 
= topSelProp
; 
 872         // Iterate through properties in-between, and select them 
 873         wxPropertyGridIterator it
; 
 875         for ( it 
= GetIterator(wxPG_ITERATE_VISIBLE
, startFrom
); 
 879             wxPGProperty
* p 
= *it
; 
 881             if ( !p
->IsCategory() && 
 882                  !m_pState
->DoIsPropertySelected(p
) ) 
 884                 DoAddToSelection(p
, selFlags
); 
 893         res 
= DoSelectAndEdit(prop
, colIndex
, selFlags
); 
 899 // ----------------------------------------------------------------------- 
 901 void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty
& newSelection
, 
 904     if ( newSelection
.size() > 0 ) 
 906         if ( !DoSelectProperty(newSelection
[0], selFlags
) ) 
 911         DoClearSelection(false, selFlags
); 
 914     for ( unsigned int i 
= 1; i 
< newSelection
.size(); i
++ ) 
 916         DoAddToSelection(newSelection
[i
], selFlags
); 
 922 // ----------------------------------------------------------------------- 
 924 void wxPropertyGrid::MakeColumnEditable( unsigned int column
, 
 927     wxASSERT( column 
!= 1 ); 
 929     wxArrayInt
& cols 
= m_pState
->m_editableColumns
; 
 933         cols
.push_back(column
); 
 937         for ( int i 
= cols
.size() - 1; i 
> 0; i
-- ) 
 939             if ( cols
[i
] == (int)column 
) 
 940                 cols
.erase( cols
.begin() + i 
); 
 945 // ----------------------------------------------------------------------- 
 947 void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex
, 
 950     wxPGProperty
* selected 
= GetSelection(); 
 951     wxCHECK_RET(selected
, wxT("No property selected")); 
 952     wxCHECK_RET(colIndex 
!= 1, wxT("Do not use this for column 1")); 
 954     if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 956         if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN
, 
 963     const wxPGCell
* cell 
= NULL
; 
 964     if ( selected
->HasCell(colIndex
) ) 
 966         cell 
= &selected
->GetCell(colIndex
); 
 967         if ( !cell
->HasText() && colIndex 
== 0 ) 
 968             text 
= selected
->GetLabel(); 
 974             text 
= selected
->GetLabel(); 
 976             cell 
= &selected
->GetOrCreateCell(colIndex
); 
 979     if ( cell 
&& cell
->HasText() ) 
 980         text 
= cell
->GetText(); 
 982     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
);  // send event 
 984     m_selColumn 
= colIndex
; 
 986     wxRect r 
= GetEditorWidgetRect(selected
, m_selColumn
); 
 988     wxWindow
* tc 
= GenerateEditorTextCtrl(r
.GetPosition(), 
 996     wxWindowID id 
= tc
->GetId(); 
 997     tc
->Connect(id
, wxEVT_COMMAND_TEXT_ENTER
, 
 998         wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress
), 
1000     tc
->Connect(id
, wxEVT_KEY_DOWN
, 
1001         wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress
), 
1006     m_labelEditor 
= wxStaticCast(tc
, wxTextCtrl
); 
1007     m_labelEditorProperty 
= selected
; 
1010 // ----------------------------------------------------------------------- 
1013 wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent
& WXUNUSED(event
) ) 
1015     DoEndLabelEdit(true); 
1018 // ----------------------------------------------------------------------- 
1020 void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent
& event 
) 
1022     int keycode 
= event
.GetKeyCode(); 
1024     if ( keycode 
== WXK_ESCAPE 
) 
1026         DoEndLabelEdit(false); 
1030         HandleKeyEvent(event
, true); 
1034 // ----------------------------------------------------------------------- 
1036 void wxPropertyGrid::DoEndLabelEdit( bool commit
, int selFlags 
) 
1038     if ( !m_labelEditor 
) 
1041     wxPGProperty
* prop 
= m_labelEditorProperty
; 
1046         if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
1048             // wxPG_SEL_NOVALIDATE is passed correctly in selFlags 
1049             if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING
, 
1050                             prop
, NULL
, selFlags
, 
1055         wxString text 
= m_labelEditor
->GetValue(); 
1056         wxPGCell
* cell 
= NULL
; 
1057         if ( prop
->HasCell(m_selColumn
) ) 
1059             cell 
= &prop
->GetCell(m_selColumn
); 
1063             if ( m_selColumn 
== 0 ) 
1064                 prop
->SetLabel(text
); 
1066                 cell 
= &prop
->GetOrCreateCell(m_selColumn
); 
1070             cell
->SetText(text
); 
1074     int wasFocused 
= m_iFlags 
& wxPG_FL_FOCUSED
; 
1076     DestroyEditorWnd(m_labelEditor
); 
1078     m_labelEditor 
= NULL
; 
1079     m_labelEditorProperty 
= NULL
; 
1081     // Fix focus (needed at least on wxGTK) 
1088 // ----------------------------------------------------------------------- 
1090 void wxPropertyGrid::SetExtraStyle( long exStyle 
) 
1092     if ( exStyle 
& wxPG_EX_ENABLE_TLP_TRACKING 
) 
1093         OnTLPChanging(::wxGetTopLevelParent(this)); 
1095         OnTLPChanging(NULL
); 
1097     if ( exStyle 
& wxPG_EX_NATIVE_DOUBLE_BUFFERING 
) 
1099 #if defined(__WXMSW__) 
1102         // Don't use WS_EX_COMPOSITED just now. 
1105         if ( m_iFlags & wxPG_FL_IN_MANAGER ) 
1106             hWnd = (HWND)GetParent()->GetHWND(); 
1108             hWnd = (HWND)GetHWND(); 
1110         ::SetWindowLong( hWnd, GWL_EXSTYLE, 
1111                          ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED ); 
1114 //#elif defined(__WXGTK20__) 
1116         // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window 
1117         // truly was double-buffered. 
1118         if ( !this->IsDoubleBuffered() ) 
1120             exStyle 
&= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING
); 
1124             wxDELETE(m_doubleBuffer
); 
1128     wxControl::SetExtraStyle( exStyle 
); 
1130     if ( exStyle 
& wxPG_EX_INIT_NOCAT 
) 
1131         m_pState
->InitNonCatMode(); 
1133     if ( exStyle 
& wxPG_EX_HELP_AS_TOOLTIPS 
) 
1134         m_windowStyle 
|= wxPG_TOOLTIPS
; 
1137     wxPGGlobalVars
->m_extraStyle 
= exStyle
; 
1140 // ----------------------------------------------------------------------- 
1142 // returns the best acceptable minimal size 
1143 wxSize 
wxPropertyGrid::DoGetBestSize() const 
1145     int lineHeight 
= wxMax(15, m_lineHeight
); 
1147     // don't make the grid too tall (limit height to 10 items) but don't 
1148     // make it too small neither 
1149     int numLines 
= wxMin
 
1151                     wxMax(m_pState
->m_properties
->GetChildCount(), 3), 
1155     wxClientDC 
dc(const_cast<wxPropertyGrid 
*>(this)); 
1156     int width 
= m_marginWidth
; 
1157     for ( unsigned int i 
= 0; i 
< m_pState
->m_colWidths
.size(); i
++ ) 
1159         width 
+= m_pState
->GetColumnFitWidth(dc
, m_pState
->DoGetRoot(), i
, true); 
1162     const wxSize sz 
= wxSize(width
, lineHeight
*numLines 
+ 40); 
1168 // ----------------------------------------------------------------------- 
1170 void wxPropertyGrid::OnTLPChanging( wxWindow
* newTLP 
) 
1172     if ( newTLP 
== m_tlp 
) 
1175     wxLongLong currentTime 
= ::wxGetLocalTimeMillis(); 
1178     // Parent changed so let's redetermine and re-hook the 
1179     // correct top-level window. 
1182         m_tlp
->Disconnect( wxEVT_CLOSE_WINDOW
, 
1183                            wxCloseEventHandler(wxPropertyGrid::OnTLPClose
), 
1185         m_tlpClosed 
= m_tlp
; 
1186         m_tlpClosedTime 
= currentTime
; 
1191         // Only accept new tlp if same one was not just dismissed. 
1192         if ( newTLP 
!= m_tlpClosed 
|| 
1193              m_tlpClosedTime
+250 < currentTime 
) 
1195             newTLP
->Connect( wxEVT_CLOSE_WINDOW
, 
1196                              wxCloseEventHandler(wxPropertyGrid::OnTLPClose
), 
1209 // ----------------------------------------------------------------------- 
1211 void wxPropertyGrid::OnTLPClose( wxCloseEvent
& event 
) 
1213     // ClearSelection forces value validation/commit. 
1214     if ( event
.CanVeto() && !DoClearSelection() ) 
1220     // Ok, it can close, set tlp pointer to NULL. Some other event 
1221     // handler can of course veto the close, but our OnIdle() should 
1222     // then be able to regain the tlp pointer. 
1223     OnTLPChanging(NULL
); 
1228 // ----------------------------------------------------------------------- 
1230 bool wxPropertyGrid::Reparent( wxWindowBase 
*newParent 
) 
1232     OnTLPChanging((wxWindow
*)newParent
); 
1234     bool res 
= wxControl::Reparent(newParent
); 
1239 // ----------------------------------------------------------------------- 
1240 // wxPropertyGrid Font and Colour Methods 
1241 // ----------------------------------------------------------------------- 
1243 void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing 
) 
1247     m_captionFont 
= wxControl::GetFont(); 
1249     GetTextExtent(wxS("jG"), &x
, &y
, 0, 0, &m_captionFont
); 
1250     m_subgroup_extramargin 
= x 
+ (x
/2); 
1253 #if wxPG_USE_RENDERER_NATIVE 
1254     m_iconWidth 
= wxPG_ICON_WIDTH
; 
1255 #elif wxPG_ICON_WIDTH 
1257     m_iconWidth 
= (m_fontHeight 
* wxPG_ICON_WIDTH
) / 13; 
1258     if ( m_iconWidth 
< 5 ) m_iconWidth 
= 5; 
1259     else if ( !(m_iconWidth 
& 0x01) ) m_iconWidth
++; // must be odd 
1263     m_gutterWidth 
= m_iconWidth 
/ wxPG_GUTTER_DIV
; 
1264     if ( m_gutterWidth 
< wxPG_GUTTER_MIN 
) 
1265         m_gutterWidth 
= wxPG_GUTTER_MIN
; 
1268     if ( vspacing 
<= 1 ) vdiv 
= 12; 
1269     else if ( vspacing 
>= 3 ) vdiv 
= 3; 
1271     m_spacingy 
= m_fontHeight 
/ vdiv
; 
1272     if ( m_spacingy 
< wxPG_YSPACING_MIN 
) 
1273         m_spacingy 
= wxPG_YSPACING_MIN
; 
1276     if ( !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
1277         m_marginWidth 
= m_gutterWidth
*2 + m_iconWidth
; 
1279     m_captionFont
.SetWeight(wxBOLD
); 
1280     GetTextExtent(wxS("jG"), &x
, &y
, 0, 0, &m_captionFont
); 
1282     m_lineHeight 
= m_fontHeight
+(2*m_spacingy
)+1; 
1285     m_buttonSpacingY 
= (m_lineHeight 
- m_iconHeight
) / 2; 
1286     if ( m_buttonSpacingY 
< 0 ) m_buttonSpacingY 
= 0; 
1289         m_pState
->CalculateFontAndBitmapStuff(vspacing
); 
1291     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
1292         RecalculateVirtualSize(); 
1294     InvalidateBestSize(); 
1297 // ----------------------------------------------------------------------- 
1299 void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent 
&WXUNUSED(event
) ) 
1305 // ----------------------------------------------------------------------- 
1307 static wxColour 
wxPGAdjustColour(const wxColour
& src
, int ra
, 
1308                                  int ga 
= 1000, int ba 
= 1000, 
1309                                  bool forceDifferent 
= false) 
1316     // Recursion guard (allow 2 max) 
1317     static int isinside 
= 0; 
1319     wxCHECK_MSG( isinside 
< 3, 
1321                  wxT("wxPGAdjustColour should not be recursively called more than once") ); 
1326     int g 
= src
.Green(); 
1329     if ( r2
>255 ) r2 
= 255; 
1330     else if ( r2
<0) r2 
= 0; 
1332     if ( g2
>255 ) g2 
= 255; 
1333     else if ( g2
<0) g2 
= 0; 
1335     if ( b2
>255 ) b2 
= 255; 
1336     else if ( b2
<0) b2 
= 0; 
1338     // Make sure they are somewhat different 
1339     if ( forceDifferent 
&& (abs((r
+g
+b
)-(r2
+g2
+b2
)) < abs(ra
/2)) ) 
1340         dst 
= wxPGAdjustColour(src
,-(ra
*2)); 
1342         dst 
= wxColour(r2
,g2
,b2
); 
1344     // Recursion guard (allow 2 max) 
1351 static int wxPGGetColAvg( const wxColour
& col 
) 
1353     return (col
.Red() + col
.Green() + col
.Blue()) / 3; 
1357 void wxPropertyGrid::RegainColours() 
1359     if ( !(m_coloursCustomized 
& 0x0002) ) 
1361         wxColour col 
= wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE 
); 
1363         // Make sure colour is dark enough 
1365         int colDec 
= wxPGGetColAvg(col
) - 230; 
1367         int colDec 
= wxPGGetColAvg(col
) - 200; 
1370             m_colCapBack 
= wxPGAdjustColour(col
,-colDec
); 
1373         m_categoryDefaultCell
.GetData()->SetBgCol(m_colCapBack
); 
1376     if ( !(m_coloursCustomized 
& 0x0001) ) 
1377         m_colMargin 
= m_colCapBack
; 
1379     if ( !(m_coloursCustomized 
& 0x0004) ) 
1386         wxColour capForeCol 
= wxPGAdjustColour(m_colCapBack
,colDec
,5000,5000,true); 
1387         m_colCapFore 
= capForeCol
; 
1388         m_categoryDefaultCell
.GetData()->SetFgCol(capForeCol
); 
1391     if ( !(m_coloursCustomized 
& 0x0008) ) 
1393         wxColour bgCol 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW 
); 
1394         m_colPropBack 
= bgCol
; 
1395         m_propertyDefaultCell
.GetData()->SetBgCol(bgCol
); 
1396         if ( !m_unspecifiedAppearance
.GetBgCol().IsOk() ) 
1397             m_unspecifiedAppearance
.SetBgCol(bgCol
); 
1400     if ( !(m_coloursCustomized 
& 0x0010) ) 
1402         wxColour fgCol 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT 
); 
1403         m_colPropFore 
= fgCol
; 
1404         m_propertyDefaultCell
.GetData()->SetFgCol(fgCol
); 
1405         if ( !m_unspecifiedAppearance
.GetFgCol().IsOk() ) 
1406             m_unspecifiedAppearance
.SetFgCol(fgCol
); 
1409     if ( !(m_coloursCustomized 
& 0x0020) ) 
1410         m_colSelBack 
= wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT 
); 
1412     if ( !(m_coloursCustomized 
& 0x0040) ) 
1413         m_colSelFore 
= wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT 
); 
1415     if ( !(m_coloursCustomized 
& 0x0080) ) 
1416         m_colLine 
= m_colCapBack
; 
1418     if ( !(m_coloursCustomized 
& 0x0100) ) 
1419         m_colDisPropFore 
= m_colCapFore
; 
1421     m_colEmptySpace 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW 
); 
1424 // ----------------------------------------------------------------------- 
1426 void wxPropertyGrid::ResetColours() 
1428     m_coloursCustomized 
= 0; 
1435 // ----------------------------------------------------------------------- 
1437 bool wxPropertyGrid::SetFont( const wxFont
& font 
) 
1439     // Must disable active editor. 
1442     bool res 
= wxControl::SetFont( font 
); 
1443     if ( res 
&& GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant 
1445         CalculateFontAndBitmapStuff( m_vspacing 
); 
1452 // ----------------------------------------------------------------------- 
1454 void wxPropertyGrid::SetLineColour( const wxColour
& col 
) 
1457     m_coloursCustomized 
|= 0x80; 
1461 // ----------------------------------------------------------------------- 
1463 void wxPropertyGrid::SetMarginColour( const wxColour
& col 
) 
1466     m_coloursCustomized 
|= 0x01; 
1470 // ----------------------------------------------------------------------- 
1472 void wxPropertyGrid::SetCellBackgroundColour( const wxColour
& col 
) 
1474     m_colPropBack 
= col
; 
1475     m_coloursCustomized 
|= 0x08; 
1477     m_propertyDefaultCell
.GetData()->SetBgCol(col
); 
1478     m_unspecifiedAppearance
.SetBgCol(col
); 
1483 // ----------------------------------------------------------------------- 
1485 void wxPropertyGrid::SetCellTextColour( const wxColour
& col 
) 
1487     m_colPropFore 
= col
; 
1488     m_coloursCustomized 
|= 0x10; 
1490     m_propertyDefaultCell
.GetData()->SetFgCol(col
); 
1491     m_unspecifiedAppearance
.SetFgCol(col
); 
1496 // ----------------------------------------------------------------------- 
1498 void wxPropertyGrid::SetEmptySpaceColour( const wxColour
& col 
) 
1500     m_colEmptySpace 
= col
; 
1505 // ----------------------------------------------------------------------- 
1507 void wxPropertyGrid::SetCellDisabledTextColour( const wxColour
& col 
) 
1509     m_colDisPropFore 
= col
; 
1510     m_coloursCustomized 
|= 0x100; 
1514 // ----------------------------------------------------------------------- 
1516 void wxPropertyGrid::SetSelectionBackgroundColour( const wxColour
& col 
) 
1519     m_coloursCustomized 
|= 0x20; 
1523 // ----------------------------------------------------------------------- 
1525 void wxPropertyGrid::SetSelectionTextColour( const wxColour
& col 
) 
1528     m_coloursCustomized 
|= 0x40; 
1532 // ----------------------------------------------------------------------- 
1534 void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour
& col 
) 
1537     m_coloursCustomized 
|= 0x02; 
1539     m_categoryDefaultCell
.GetData()->SetBgCol(col
); 
1544 // ----------------------------------------------------------------------- 
1546 void wxPropertyGrid::SetCaptionTextColour( const wxColour
& col 
) 
1549     m_coloursCustomized 
|= 0x04; 
1551     m_categoryDefaultCell
.GetData()->SetFgCol(col
); 
1556 // ----------------------------------------------------------------------- 
1557 // wxPropertyGrid property adding and removal 
1558 // ----------------------------------------------------------------------- 
1560 void wxPropertyGrid::PrepareAfterItemsAdded() 
1562     if ( !m_pState 
|| !m_pState
->m_itemsAdded 
) return; 
1564     m_pState
->m_itemsAdded 
= 0; 
1566     if ( m_windowStyle 
& wxPG_AUTO_SORT 
) 
1567         Sort(wxPG_SORT_TOP_LEVEL_ONLY
); 
1569     RecalculateVirtualSize(); 
1571     // Fix editor position 
1572     CorrectEditorWidgetPosY(); 
1575 // ----------------------------------------------------------------------- 
1576 // wxPropertyGrid property operations 
1577 // ----------------------------------------------------------------------- 
1579 bool wxPropertyGrid::EnsureVisible( wxPGPropArg id 
) 
1581     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
1585     bool changed 
= false; 
1587     // Is it inside collapsed section? 
1588     if ( !p
->IsVisible() ) 
1591         wxPGProperty
* parent 
= p
->GetParent(); 
1592         wxPGProperty
* grandparent 
= parent
->GetParent(); 
1594         if ( grandparent 
&& grandparent 
!= m_pState
->m_properties 
) 
1595             Expand( grandparent 
); 
1603     GetViewStart(&vx
,&vy
); 
1604     vy
*=wxPG_PIXELS_PER_UNIT
; 
1610         Scroll(vx
, y
/wxPG_PIXELS_PER_UNIT 
); 
1611         m_iFlags 
|= wxPG_FL_SCROLLED
; 
1614     else if ( (y
+m_lineHeight
) > (vy
+m_height
) ) 
1616         Scroll(vx
, (y
-m_height
+(m_lineHeight
*2))/wxPG_PIXELS_PER_UNIT 
); 
1617         m_iFlags 
|= wxPG_FL_SCROLLED
; 
1627 // ----------------------------------------------------------------------- 
1628 // wxPropertyGrid helper methods called by properties 
1629 // ----------------------------------------------------------------------- 
1631 // Control font changer helper. 
1632 void wxPropertyGrid::SetCurControlBoldFont() 
1634     wxWindow
* editor 
= GetEditorControl(); 
1635     editor
->SetFont( m_captionFont 
); 
1638 // ----------------------------------------------------------------------- 
1640 wxPoint 
wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty
* p
, 
1643 #if wxPG_SMALL_SCREEN 
1644     // On small-screen devices, always show dialogs with default position and size. 
1645     return wxDefaultPosition
; 
1647     int splitterX 
= GetSplitterPosition(); 
1651     wxCHECK_MSG( y 
>= 0, wxPoint(-1,-1), wxT("invalid y?") ); 
1653     ImprovedClientToScreen( &x
, &y 
); 
1655     int sw 
= wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X 
); 
1656     int sh 
= wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y 
); 
1663         new_x 
= x 
+ (m_width
-splitterX
) - sz
.x
; 
1673         new_y 
= y 
+ m_lineHeight
; 
1675     return wxPoint(new_x
,new_y
); 
1679 // ----------------------------------------------------------------------- 
1681 wxString
& wxPropertyGrid::ExpandEscapeSequences( wxString
& dst_str
, wxString
& src_str 
) 
1683     if ( src_str
.empty() ) 
1689     bool prev_is_slash 
= false; 
1691     wxString::const_iterator i 
= src_str
.begin(); 
1695     for ( ; i 
!= src_str
.end(); ++i 
) 
1699         if ( a 
!= wxS('\\') ) 
1701             if ( !prev_is_slash 
) 
1707                 if ( a 
== wxS('n') ) 
1710                     dst_str 
<< wxS('\n'); 
1712                     dst_str 
<< wxS('\n'); 
1715                 else if ( a 
== wxS('t') ) 
1716                     dst_str 
<< wxS('\t'); 
1720             prev_is_slash 
= false; 
1724             if ( prev_is_slash 
) 
1726                 dst_str 
<< wxS('\\'); 
1727                 prev_is_slash 
= false; 
1731                 prev_is_slash 
= true; 
1738 // ----------------------------------------------------------------------- 
1740 wxString
& wxPropertyGrid::CreateEscapeSequences( wxString
& dst_str
, wxString
& src_str 
) 
1742     if ( src_str
.empty() ) 
1748     wxString::const_iterator i 
= src_str
.begin(); 
1749     wxUniChar prev_a 
= wxS('\0'); 
1753     for ( ; i 
!= src_str
.end(); ++i 
) 
1757         if ( a 
>= wxS(' ') ) 
1759             // This surely is not something that requires an escape sequence. 
1764             // This might need... 
1765             if ( a 
== wxS('\r')  ) 
1767                 // DOS style line end. 
1768                 // Already taken care below 
1770             else if ( a 
== wxS('\n') ) 
1771                 // UNIX style line end. 
1772                 dst_str 
<< wxS("\\n"); 
1773             else if ( a 
== wxS('\t') ) 
1775                 dst_str 
<< wxS('\t'); 
1778                 //wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a); 
1788 // ----------------------------------------------------------------------- 
1790 wxPGProperty
* wxPropertyGrid::DoGetItemAtY( int y 
) const 
1797     return m_pState
->m_properties
->GetItemAtY(y
, m_lineHeight
, &a
); 
1800 // ----------------------------------------------------------------------- 
1801 // wxPropertyGrid graphics related methods 
1802 // ----------------------------------------------------------------------- 
1804 void wxPropertyGrid::OnPaint( wxPaintEvent
& WXUNUSED(event
) ) 
1809     // Don't paint after destruction has begun 
1810     if ( !HasInternalFlag(wxPG_FL_INITIALIZED
) ) 
1813     // Find out where the window is scrolled to 
1814     int vx
,vy
;                     // Top left corner of client 
1815     GetViewStart(&vx
,&vy
); 
1816     vy 
*= wxPG_PIXELS_PER_UNIT
; 
1818     // Update everything inside the box 
1819     wxRect r 
= GetUpdateRegion().GetBox(); 
1823     // FIXME: This is just a workaround for a bug that causes splitters not 
1824     //        to paint when other windows are being dragged over the grid. 
1826     r
.width 
= GetClientSize().x
; 
1829     r
.height 
= GetClientSize().y
; 
1831     // Repaint this rectangle 
1832     DrawItems( dc
, r
.y
, r
.y 
+ r
.height
, &r 
); 
1834     // We assume that the size set when grid is shown 
1835     // is what is desired. 
1836     SetInternalFlag(wxPG_FL_GOOD_SIZE_SET
); 
1839 // ----------------------------------------------------------------------- 
1841 void wxPropertyGrid::DrawExpanderButton( wxDC
& dc
, const wxRect
& rect
, 
1842                                          wxPGProperty
* property 
) const 
1844     // Prepare rectangle to be used 
1846     r
.x 
+= m_gutterWidth
; r
.y 
+= m_buttonSpacingY
; 
1847     r
.width 
= m_iconWidth
; r
.height 
= m_iconHeight
; 
1849 #if (wxPG_USE_RENDERER_NATIVE) 
1851 #elif wxPG_ICON_WIDTH 
1852     // Drawing expand/collapse button manually 
1853     dc
.SetPen(m_colPropFore
); 
1854     if ( property
->IsCategory() ) 
1855         dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
1857         dc
.SetBrush(m_colPropBack
); 
1859     dc
.DrawRectangle( r 
); 
1860     int _y 
= r
.y
+(m_iconWidth
/2); 
1861     dc
.DrawLine(r
.x
+2,_y
,r
.x
+m_iconWidth
-2,_y
); 
1866     if ( property
->IsExpanded() ) 
1868     // wxRenderer functions are non-mutating in nature, so it 
1869     // should be safe to cast "const wxPropertyGrid*" to "wxWindow*". 
1870     // Hopefully this does not cause problems. 
1871     #if (wxPG_USE_RENDERER_NATIVE) 
1872         wxRendererNative::Get().DrawTreeItemButton( 
1878     #elif wxPG_ICON_WIDTH 
1887     #if (wxPG_USE_RENDERER_NATIVE) 
1888         wxRendererNative::Get().DrawTreeItemButton( 
1894     #elif wxPG_ICON_WIDTH 
1895         int _x 
= r
.x
+(m_iconWidth
/2); 
1896         dc
.DrawLine(_x
,r
.y
+2,_x
,r
.y
+m_iconWidth
-2); 
1902 #if (wxPG_USE_RENDERER_NATIVE) 
1904 #elif wxPG_ICON_WIDTH 
1907     dc
.DrawBitmap( *bmp
, r
.x
, r
.y
, true ); 
1911 // ----------------------------------------------------------------------- 
1914 // This is the one called by OnPaint event handler and others. 
1915 // topy and bottomy are already unscrolled (ie. physical) 
1917 void wxPropertyGrid::DrawItems( wxDC
& dc
, 
1918                                 unsigned int topItemY
, 
1919                                 unsigned int bottomItemY
, 
1920                                 const wxRect
* itemsRect 
) 
1924          bottomItemY 
< topItemY 
|| 
1928     m_pState
->EnsureVirtualHeight(); 
1930     wxRect tempItemsRect
; 
1933         tempItemsRect 
= wxRect(0, topItemY
, 
1936         itemsRect 
= &tempItemsRect
; 
1940     GetViewStart(&vx
, &vy
); 
1941     vx 
*= wxPG_PIXELS_PER_UNIT
; 
1942     vy 
*= wxPG_PIXELS_PER_UNIT
; 
1944     // itemRect is in virtual grid space 
1945     wxRect 
drawRect(itemsRect
->x 
- vx
, 
1950     // items added check 
1951     if ( m_pState
->m_itemsAdded 
) PrepareAfterItemsAdded(); 
1953     int paintFinishY 
= 0; 
1955     if ( m_pState
->m_properties
->GetChildCount() > 0 ) 
1958         bool isBuffered 
= false; 
1960         wxMemoryDC
* bufferDC 
= NULL
; 
1962         if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING
) ) 
1964             if ( !m_doubleBuffer 
) 
1966                 paintFinishY 
= itemsRect
->y
; 
1971                 bufferDC 
= new wxMemoryDC(); 
1973                 // If nothing was changed, then just copy from double-buffer 
1974                 bufferDC
->SelectObject( *m_doubleBuffer 
); 
1983             // paintFinishY and drawBottomY are in buffer/physical space 
1984             paintFinishY 
= DoDrawItems( *dcPtr
, itemsRect
, isBuffered 
); 
1985             int drawBottomY 
= itemsRect
->y 
+ itemsRect
->height 
- vy
; 
1987             // Clear area beyond last painted property 
1988             if ( paintFinishY 
< drawBottomY 
) 
1990                 dcPtr
->SetPen(m_colEmptySpace
); 
1991                 dcPtr
->SetBrush(m_colEmptySpace
); 
1992                 dcPtr
->DrawRectangle(0, paintFinishY
, 
2000             dc
.Blit( drawRect
.x
, drawRect
.y
, drawRect
.width
, 
2002                      bufferDC
, 0, 0, wxCOPY 
); 
2008         // Just clear the area 
2009         dc
.SetPen(m_colEmptySpace
); 
2010         dc
.SetBrush(m_colEmptySpace
); 
2011         dc
.DrawRectangle(drawRect
); 
2015 // ----------------------------------------------------------------------- 
2017 int wxPropertyGrid::DoDrawItems( wxDC
& dc
, 
2018                                  const wxRect
* itemsRect
, 
2019                                  bool isBuffered 
) const 
2021     const wxPGProperty
* firstItem
; 
2022     const wxPGProperty
* lastItem
; 
2024     firstItem 
= DoGetItemAtY(itemsRect
->y
); 
2025     lastItem 
= DoGetItemAtY(itemsRect
->y
+itemsRect
->height
-1); 
2028         lastItem 
= GetLastItem( wxPG_ITERATE_VISIBLE 
); 
2030     if ( m_frozen 
|| m_height 
< 1 || firstItem 
== NULL 
) 
2031         return itemsRect
->y
; 
2033     wxCHECK_MSG( !m_pState
->m_itemsAdded
, itemsRect
->y
, 
2035     wxASSERT( m_pState
->m_properties
->GetChildCount() ); 
2037     int lh 
= m_lineHeight
; 
2040     int lastItemBottomY
; 
2042     firstItemTopY 
= itemsRect
->y
; 
2043     lastItemBottomY 
= itemsRect
->y 
+ itemsRect
->height
; 
2045     // Align y coordinates to item boundaries 
2046     firstItemTopY 
-= firstItemTopY 
% lh
; 
2047     lastItemBottomY 
+= lh 
- (lastItemBottomY 
% lh
); 
2048     lastItemBottomY 
-= 1; 
2050     // Entire range outside scrolled, visible area? 
2051     if ( firstItemTopY 
>= (int)m_pState
->GetVirtualHeight() || 
2052          lastItemBottomY 
<= 0 ) 
2053         return itemsRect
->y
; 
2055     wxCHECK_MSG( firstItemTopY 
< lastItemBottomY
, 
2057                  "invalid y values" ); 
2060     wxLogDebug("  -> DoDrawItems ( \"%s\" -> \"%s\" 
2061                "height=%i (ch=%i), itemsRect = 0x%lX )", 
2062         firstItem->GetLabel().c_str(), 
2063         lastItem->GetLabel().c_str(), 
2064         (int)(lastItemBottomY - firstItemTopY), 
2066         (unsigned long)&itemsRect ); 
2071     long windowStyle 
= m_windowStyle
; 
2076     // For now, do some manual calculation for double buffering 
2077     // - buffer's y = 0, so align itemsRect and coordinates to that 
2079     // TODO: In future use wxAutoBufferedPaintDC (for example) 
2087         xRelMod 
= itemsRect
->x
; 
2088         yRelMod 
= itemsRect
->y
; 
2091         // itemsRect conversion 
2096         firstItemTopY 
-= yRelMod
; 
2097         lastItemBottomY 
-= yRelMod
; 
2100     int x 
= m_marginWidth 
- xRelMod
; 
2102     wxFont normalFont 
= GetFont(); 
2104     bool reallyFocused 
= (m_iFlags 
& wxPG_FL_FOCUSED
) != 0; 
2106     bool isPgEnabled 
= IsEnabled(); 
2109     // Prepare some pens and brushes that are often changed to. 
2112     wxBrush 
marginBrush(m_colMargin
); 
2113     wxPen 
marginPen(m_colMargin
); 
2114     wxBrush 
capbgbrush(m_colCapBack
,wxSOLID
); 
2115     wxPen 
linepen(m_colLine
,1,wxSOLID
); 
2117     wxColour selBackCol
; 
2119         selBackCol 
= m_colSelBack
; 
2121         selBackCol 
= m_colMargin
; 
2123     // pen that has same colour as text 
2124     wxPen 
outlinepen(m_colPropFore
,1,wxSOLID
); 
2127     // Clear margin with background colour 
2129     dc
.SetBrush( marginBrush 
); 
2130     if ( !(windowStyle 
& wxPG_HIDE_MARGIN
) ) 
2132         dc
.SetPen( *wxTRANSPARENT_PEN 
); 
2133         dc
.DrawRectangle(-1-xRelMod
,firstItemTopY
-1,x
+2,lastItemBottomY
-firstItemTopY
+2); 
2136     const wxPGProperty
* firstSelected 
= GetSelection(); 
2137     const wxPropertyGridPageState
* state 
= m_pState
; 
2138     const wxArrayInt
& colWidths 
= state
->m_colWidths
; 
2140     // TODO: Only render columns that are within clipping region. 
2142     dc
.SetFont(normalFont
); 
2144     wxPropertyGridConstIterator 
it( state
, wxPG_ITERATE_VISIBLE
, firstItem 
); 
2145     int endScanBottomY 
= lastItemBottomY 
+ lh
; 
2146     int y 
= firstItemTopY
; 
2149     // Pregenerate list of visible properties. 
2150     wxArrayPGProperty visPropArray
; 
2151     visPropArray
.reserve((m_height
/m_lineHeight
)+6); 
2153     for ( ; !it
.AtEnd(); it
.Next() ) 
2155         const wxPGProperty
* p 
= *it
; 
2157         if ( !p
->HasFlag(wxPG_PROP_HIDDEN
) ) 
2159             visPropArray
.push_back((wxPGProperty
*)p
); 
2161             if ( y 
> endScanBottomY 
) 
2168     visPropArray
.push_back(NULL
); 
2170     wxPGProperty
* nextP 
= visPropArray
[0]; 
2172     int gridWidth 
= state
->m_width
; 
2175     for ( unsigned int arrInd
=1; 
2176           nextP 
&& y 
<= lastItemBottomY
; 
2179         wxPGProperty
* p 
= nextP
; 
2180         nextP 
= visPropArray
[arrInd
]; 
2182         int rowHeight 
= m_fontHeight
+(m_spacingy
*2)+1; 
2183         int textMarginHere 
= x
; 
2184         int renderFlags 
= 0; 
2186         int greyDepth 
= m_marginWidth
; 
2187         if ( !(windowStyle 
& wxPG_HIDE_CATEGORIES
) ) 
2188             greyDepth 
= (((int)p
->m_depthBgCol
)-1) * m_subgroup_extramargin 
+ m_marginWidth
; 
2190         int greyDepthX 
= greyDepth 
- xRelMod
; 
2192         // Use basic depth if in non-categoric mode and parent is base array. 
2193         if ( !(windowStyle 
& wxPG_HIDE_CATEGORIES
) || p
->GetParent() != m_pState
->m_properties 
) 
2195             textMarginHere 
+= ((unsigned int)((p
->m_depth
-1)*m_subgroup_extramargin
)); 
2198         // Paint margin area 
2199         dc
.SetBrush(marginBrush
); 
2200         dc
.SetPen(marginPen
); 
2201         dc
.DrawRectangle( -xRelMod
, y
, greyDepth
, lh 
); 
2203         dc
.SetPen( linepen 
); 
2209         // Modified by JACS to not draw a margin if wxPG_HIDE_MARGIN is specified, since it 
2210         // looks better, at least under Windows when we have a themed border (the themed-window-specific 
2211         // whitespace between the real border and the propgrid margin exacerbates the double-border look). 
2213         // Is this or its parent themed? 
2214         bool suppressMarginEdge 
= (GetWindowStyle() & wxPG_HIDE_MARGIN
) && 
2215             (((GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_THEME
) || 
2216             (((GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_NONE
) && ((GetParent()->GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_THEME
))); 
2218         bool suppressMarginEdge 
= false; 
2220         if (!suppressMarginEdge
) 
2221             dc
.DrawLine( greyDepthX
, y
, greyDepthX
, y2 
); 
2224             // Blank out the margin edge 
2225             dc
.SetPen(wxPen(GetBackgroundColour())); 
2226             dc
.DrawLine( greyDepthX
, y
, greyDepthX
, y2 
); 
2227             dc
.SetPen( linepen 
); 
2234         for ( si
=0; si
<colWidths
.size(); si
++ ) 
2236             sx 
+= colWidths
[si
]; 
2237             dc
.DrawLine( sx
, y
, sx
, y2 
); 
2240         // Horizontal Line, below 
2241         //   (not if both this and next is category caption) 
2242         if ( p
->IsCategory() && 
2243              nextP 
&& nextP
->IsCategory() ) 
2244             dc
.SetPen(m_colCapBack
); 
2246         dc
.DrawLine( greyDepthX
, y2
-1, gridWidth
-xRelMod
, y2
-1 ); 
2249         // Need to override row colours? 
2253         bool isSelected 
= state
->DoIsPropertySelected(p
); 
2257             // Disabled may get different colour. 
2258             if ( !p
->IsEnabled() ) 
2260                 renderFlags 
|= wxPGCellRenderer::Disabled 
| 
2261                                wxPGCellRenderer::DontUseCellFgCol
; 
2262                 rowFgCol 
= m_colDisPropFore
; 
2267             renderFlags 
|= wxPGCellRenderer::Selected
; 
2269             if ( !p
->IsCategory() ) 
2271                 renderFlags 
|= wxPGCellRenderer::DontUseCellFgCol 
| 
2272                                wxPGCellRenderer::DontUseCellBgCol
; 
2274                 if ( reallyFocused 
&& p 
== firstSelected 
) 
2276                     rowFgCol 
= m_colSelFore
; 
2277                     rowBgCol 
= selBackCol
; 
2279                 else if ( isPgEnabled 
) 
2281                     rowFgCol 
= m_colPropFore
; 
2282                     if ( p 
== firstSelected 
) 
2283                         rowBgCol 
= m_colMargin
; 
2285                         rowBgCol 
= selBackCol
; 
2289                     rowFgCol 
= m_colDisPropFore
; 
2290                     rowBgCol 
= selBackCol
; 
2297         if ( rowBgCol
.IsOk() ) 
2298             rowBgBrush 
= wxBrush(rowBgCol
); 
2300         if ( HasInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
) ) 
2301             renderFlags 
= renderFlags 
& ~wxPGCellRenderer::DontUseCellColours
; 
2304         // Fill additional margin area with background colour of first cell 
2305         if ( greyDepthX 
< textMarginHere 
) 
2307             if ( !(renderFlags 
& wxPGCellRenderer::DontUseCellBgCol
) ) 
2309                 wxPGCell
& cell 
= p
->GetCell(0); 
2310                 rowBgCol 
= cell
.GetBgCol(); 
2311                 rowBgBrush 
= wxBrush(rowBgCol
); 
2313             dc
.SetBrush(rowBgBrush
); 
2314             dc
.SetPen(rowBgCol
); 
2315             dc
.DrawRectangle(greyDepthX
+1, y
, 
2316                              textMarginHere
-greyDepthX
, lh
-1); 
2319         bool fontChanged 
= false; 
2321         // Expander button rectangle 
2322         wxRect 
butRect( ((p
->m_depth 
- 1) * m_subgroup_extramargin
) - xRelMod
, 
2327         // Default cell rect fill the entire row 
2328         wxRect 
cellRect(greyDepthX
, y
, 
2329                         gridWidth 
- greyDepth 
+ 2, rowHeight
-1 ); 
2331         bool isCategory 
= p
->IsCategory(); 
2335             dc
.SetFont(m_captionFont
); 
2338             if ( renderFlags 
& wxPGCellRenderer::DontUseCellBgCol 
) 
2340                 dc
.SetBrush(rowBgBrush
); 
2341                 dc
.SetPen(rowBgCol
); 
2344             if ( renderFlags 
& wxPGCellRenderer::DontUseCellFgCol 
) 
2346                 dc
.SetTextForeground(rowFgCol
); 
2351             // Fine tune button rectangle to actually fit the cell 
2352             if ( butRect
.x 
> 0 ) 
2353                 butRect
.x 
+= IN_CELL_EXPANDER_BUTTON_X_ADJUST
; 
2355             if ( p
->m_flags 
& wxPG_PROP_MODIFIED 
&& 
2356                  (windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
2358                 dc
.SetFont(m_captionFont
); 
2362             // Magic fine-tuning for non-category rows 
2366         int firstCellWidth 
= colWidths
[0] - (greyDepthX 
- m_marginWidth
); 
2367         int firstCellX 
= cellRect
.x
; 
2369         // Calculate cellRect.x for the last cell 
2370         unsigned int ci 
= 0; 
2372         for ( ci
=0; ci
<colWidths
.size(); ci
++ ) 
2373             cellX 
+= colWidths
[ci
]; 
2376         // Draw cells from back to front so that we can easily tell if the 
2377         // cell on the right was empty from text 
2378         bool prevFilled 
= true; 
2379         ci 
= colWidths
.size(); 
2388                 textXAdd 
= textMarginHere 
- greyDepthX
; 
2389                 cellRect
.width 
= firstCellWidth
; 
2390                 cellRect
.x 
= firstCellX
; 
2394                 int colWidth 
= colWidths
[ci
]; 
2395                 cellRect
.width 
= colWidth
; 
2396                 cellRect
.x 
-= colWidth
; 
2399             // Merge with column to the right? 
2400             if ( !prevFilled 
&& isCategory 
) 
2402                 cellRect
.width 
+= colWidths
[ci
+1]; 
2406                 cellRect
.width 
-= 1; 
2408             wxWindow
* cellEditor 
= NULL
; 
2409             int cellRenderFlags 
= renderFlags
; 
2411             // Tree Item Button (must be drawn before clipping is set up) 
2412             if ( ci 
== 0 && !HasFlag(wxPG_HIDE_MARGIN
) && p
->HasVisibleChildren() ) 
2413                 DrawExpanderButton( dc
, butRect
, p 
); 
2416             if ( isSelected 
&& (ci 
== 1 || ci 
== m_selColumn
) ) 
2418                 if ( p 
== firstSelected 
) 
2420                     if ( ci 
== 1 && m_wndEditor 
) 
2421                         cellEditor 
= m_wndEditor
; 
2422                     else if ( ci 
== m_selColumn 
&& m_labelEditor 
) 
2423                         cellEditor 
= m_labelEditor
; 
2428                     wxColour editorBgCol 
= 
2429                         cellEditor
->GetBackgroundColour(); 
2430                     dc
.SetBrush(editorBgCol
); 
2431                     dc
.SetPen(editorBgCol
); 
2432                     dc
.SetTextForeground(m_colPropFore
); 
2433                     dc
.DrawRectangle(cellRect
); 
2435                     if ( m_dragStatus 
!= 0 || 
2436                          (m_iFlags 
& wxPG_FL_CUR_USES_CUSTOM_IMAGE
) ) 
2441                     dc
.SetBrush(m_colPropBack
); 
2442                     dc
.SetPen(m_colPropBack
); 
2443                     if ( p
->IsEnabled() ) 
2444                         dc
.SetTextForeground(m_colPropFore
); 
2446                         dc
.SetTextForeground(m_colDisPropFore
); 
2451                 if ( renderFlags 
& wxPGCellRenderer::DontUseCellBgCol 
) 
2453                     dc
.SetBrush(rowBgBrush
); 
2454                     dc
.SetPen(rowBgCol
); 
2457                 if ( renderFlags 
& wxPGCellRenderer::DontUseCellFgCol 
) 
2459                     dc
.SetTextForeground(rowFgCol
); 
2463             dc
.SetClippingRegion(cellRect
); 
2465             cellRect
.x 
+= textXAdd
; 
2466             cellRect
.width 
-= textXAdd
; 
2471                 wxPGCellRenderer
* renderer
; 
2472                 int cmnVal 
= p
->GetCommonValue(); 
2473                 if ( cmnVal 
== -1 || ci 
!= 1 ) 
2475                     renderer 
= p
->GetCellRenderer(ci
); 
2476                     prevFilled 
= renderer
->Render(dc
, cellRect
, this, 
2482                     renderer 
= GetCommonValue(cmnVal
)->GetRenderer(); 
2483                     prevFilled 
= renderer
->Render(dc
, cellRect
, this, 
2493             dc
.DestroyClippingRegion(); // Is this really necessary? 
2498             dc
.SetFont(normalFont
); 
2506 // ----------------------------------------------------------------------- 
2508 wxRect 
wxPropertyGrid::GetPropertyRect( const wxPGProperty
* p1
, const wxPGProperty
* p2 
) const 
2512     if ( m_width 
< 10 || m_height 
< 10 || 
2513          !m_pState
->m_properties
->GetChildCount() || 
2515         return wxRect(0,0,0,0); 
2520     // Return rect which encloses the given property range 
2521     // (in logical grid coordinates) 
2524     int visTop 
= p1
->GetY(); 
2527         visBottom 
= p2
->GetY() + m_lineHeight
; 
2529         visBottom 
= m_height 
+ visTop
; 
2531     // If seleced property is inside the range, we'll extend the range to include 
2533     wxPGProperty
* selected 
= GetSelection(); 
2536         int selectedY 
= selected
->GetY(); 
2537         if ( selectedY 
>= visTop 
&& selectedY 
< visBottom 
) 
2539             wxWindow
* editor 
= GetEditorControl(); 
2542                 int visBottom2 
= selectedY 
+ editor
->GetSize().y
; 
2543                 if ( visBottom2 
> visBottom 
) 
2544                     visBottom 
= visBottom2
; 
2549     return wxRect(0,visTop
-vy
,m_pState
->m_width
,visBottom
-visTop
); 
2552 // ----------------------------------------------------------------------- 
2554 void wxPropertyGrid::DrawItems( const wxPGProperty
* p1
, const wxPGProperty
* p2 
) 
2559     if ( m_pState
->m_itemsAdded 
) 
2560         PrepareAfterItemsAdded(); 
2562     wxRect r 
= GetPropertyRect(p1
, p2
); 
2565         // Convert rectangle from logical grid coordinates to physical ones 
2567         GetViewStart(&vx
, &vy
); 
2568         vx 
*= wxPG_PIXELS_PER_UNIT
; 
2569         vy 
*= wxPG_PIXELS_PER_UNIT
; 
2576 // ----------------------------------------------------------------------- 
2578 void wxPropertyGrid::RefreshProperty( wxPGProperty
* p 
) 
2580     if ( m_pState
->DoIsPropertySelected(p
) || p
->IsChildSelected(true) ) 
2582         // NB: We must copy the selection. 
2583         wxArrayPGProperty selection 
= m_pState
->m_selection
; 
2584         DoSetSelection(selection
, wxPG_SEL_FORCE
); 
2587     DrawItemAndChildren(p
); 
2590 // ----------------------------------------------------------------------- 
2592 void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty
* p 
) 
2597     // Draw item, children, and parent too, if it is not category 
2598     wxPGProperty
* parent 
= p
->GetParent(); 
2601             !parent
->IsCategory() && 
2602             parent
->GetParent() ) 
2605          parent 
= parent
->GetParent(); 
2608     DrawItemAndChildren(p
); 
2611 void wxPropertyGrid::DrawItemAndChildren( wxPGProperty
* p 
) 
2613     wxCHECK_RET( p
, wxT("invalid property id") ); 
2615     // Do not draw if in non-visible page 
2616     if ( p
->GetParentState() != m_pState 
) 
2619     // do not draw a single item if multiple pending 
2620     if ( m_pState
->m_itemsAdded 
|| m_frozen 
) 
2623     // Update child control. 
2624     wxPGProperty
* selected 
= GetSelection(); 
2625     if ( selected 
&& selected
->GetParent() == p 
) 
2628     const wxPGProperty
* lastDrawn 
= p
->GetLastVisibleSubItem(); 
2630     DrawItems(p
, lastDrawn
); 
2633 // ----------------------------------------------------------------------- 
2635 void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground
), 
2636                               const wxRect 
*rect 
) 
2638     PrepareAfterItemsAdded(); 
2640     wxWindow::Refresh(false, rect
); 
2642 #if wxPG_REFRESH_CONTROLS 
2643     // I think this really helps only GTK+1.2 
2644     if ( m_wndEditor 
) m_wndEditor
->Refresh(); 
2645     if ( m_wndEditor2 
) m_wndEditor2
->Refresh(); 
2649 // ----------------------------------------------------------------------- 
2650 // wxPropertyGrid global operations 
2651 // ----------------------------------------------------------------------- 
2653 void wxPropertyGrid::Clear() 
2655     m_pState
->DoClear(); 
2661     RecalculateVirtualSize(); 
2663     // Need to clear some area at the end 
2665         RefreshRect(wxRect(0, 0, m_width
, m_height
)); 
2668 // ----------------------------------------------------------------------- 
2670 bool wxPropertyGrid::EnableCategories( bool enable 
) 
2677         // Enable categories 
2680         m_windowStyle 
&= ~(wxPG_HIDE_CATEGORIES
); 
2685         // Disable categories 
2687         m_windowStyle 
|= wxPG_HIDE_CATEGORIES
; 
2690     if ( !m_pState
->EnableCategories(enable
) ) 
2695         if ( m_windowStyle 
& wxPG_AUTO_SORT 
) 
2697             m_pState
->m_itemsAdded 
= 1; // force 
2698             PrepareAfterItemsAdded(); 
2702         m_pState
->m_itemsAdded 
= 1; 
2704     // No need for RecalculateVirtualSize() here - it is already called in 
2705     // wxPropertyGridPageState method above. 
2712 // ----------------------------------------------------------------------- 
2714 void wxPropertyGrid::SwitchState( wxPropertyGridPageState
* pNewState 
) 
2716     wxASSERT( pNewState 
); 
2717     wxASSERT( pNewState
->GetGrid() ); 
2719     if ( pNewState 
== m_pState 
) 
2722     wxArrayPGProperty oldSelection 
= m_pState
->m_selection
; 
2724     // Call ClearSelection() instead of DoClearSelection() 
2725     // so that selection clear events are not sent. 
2728     m_pState
->m_selection 
= oldSelection
; 
2730     bool orig_mode 
= m_pState
->IsInNonCatMode(); 
2731     bool new_state_mode 
= pNewState
->IsInNonCatMode(); 
2733     m_pState 
= pNewState
; 
2736     int pgWidth 
= GetClientSize().x
; 
2737     if ( HasVirtualWidth() ) 
2739         int minWidth 
= pgWidth
; 
2740         if ( pNewState
->m_width 
< minWidth 
) 
2742             pNewState
->m_width 
= minWidth
; 
2743             pNewState
->CheckColumnWidths(); 
2749         // Just in case, fully re-center splitter 
2750         //if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) ) 
2751         //    pNewState->m_fSplitterX = -1.0; 
2753         pNewState
->OnClientWidthChange(pgWidth
, 
2754                                        pgWidth 
- pNewState
->m_width
); 
2759     // If necessary, convert state to correct mode. 
2760     if ( orig_mode 
!= new_state_mode 
) 
2762         // This should refresh as well. 
2763         EnableCategories( orig_mode
?false:true ); 
2765     else if ( !m_frozen 
) 
2767         // Refresh, if not frozen. 
2768         m_pState
->PrepareAfterItemsAdded(); 
2770         // Reselect (Use SetSelection() instead of Do-variant so that 
2771         // events won't be sent). 
2772         SetSelection(m_pState
->m_selection
); 
2774         RecalculateVirtualSize(0); 
2778         m_pState
->m_itemsAdded 
= 1; 
2781 // ----------------------------------------------------------------------- 
2783 // Call to SetSplitterPosition will always disable splitter auto-centering 
2784 // if parent window is shown. 
2785 void wxPropertyGrid::DoSetSplitterPosition( int newxpos
, 
2789     if ( ( newxpos 
< wxPG_DRAG_MARGIN 
) ) 
2792     wxPropertyGridPageState
* state 
= m_pState
; 
2794     if ( flags 
& wxPG_SPLITTER_FROM_EVENT 
) 
2795         state
->m_dontCenterSplitter 
= true; 
2797     state
->DoSetSplitterPosition(newxpos
, splitterIndex
, flags
); 
2799     if ( flags 
& wxPG_SPLITTER_REFRESH 
) 
2801         if ( GetSelection() ) 
2802             CorrectEditorWidgetSizeX(); 
2810 // ----------------------------------------------------------------------- 
2812 void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing 
) 
2814     wxPropertyGridPageState
* state 
= m_pState
; 
2816         state
->ResetColumnSizes(0); 
2818     if ( enableAutoResizing 
&& HasFlag(wxPG_SPLITTER_AUTO_CENTER
) ) 
2819         m_pState
->m_dontCenterSplitter 
= false; 
2822 // ----------------------------------------------------------------------- 
2824 void wxPropertyGrid::CenterSplitter( bool enableAutoResizing 
) 
2826     SetSplitterPosition( m_width
/2 ); 
2827     if ( enableAutoResizing 
&& HasFlag(wxPG_SPLITTER_AUTO_CENTER
) ) 
2828         m_pState
->m_dontCenterSplitter 
= false; 
2831 // ----------------------------------------------------------------------- 
2832 // wxPropertyGrid item iteration (GetNextProperty etc.) methods 
2833 // ----------------------------------------------------------------------- 
2835 // Returns nearest paint visible property (such that will be painted unless 
2836 // window is scrolled or resized). If given property is paint visible, then 
2837 // it itself will be returned 
2838 wxPGProperty
* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty
* p 
) const 
2840     int vx
,vy1
;// Top left corner of client 
2841     GetViewStart(&vx
,&vy1
); 
2842     vy1 
*= wxPG_PIXELS_PER_UNIT
; 
2844     int vy2 
= vy1 
+ m_height
; 
2845     int propY 
= p
->GetY2(m_lineHeight
); 
2847     if ( (propY 
+ m_lineHeight
) < vy1 
) 
2850         return DoGetItemAtY( vy1 
); 
2852     else if ( propY 
> vy2 
) 
2855         return DoGetItemAtY( vy2 
); 
2858     // Itself paint visible 
2863 // ----------------------------------------------------------------------- 
2864 // Methods related to change in value, value modification and sending events 
2865 // ----------------------------------------------------------------------- 
2867 // commits any changes in editor of selected property 
2868 // return true if validation did not fail 
2869 // flags are same as with DoSelectProperty 
2870 bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags 
) 
2872     // Committing already? 
2873     if ( m_inCommitChangesFromEditor 
) 
2876     // Don't do this if already processing editor event. It might 
2877     // induce recursive dialogs and crap like that. 
2878     if ( m_iFlags 
& wxPG_FL_IN_HANDLECUSTOMEDITOREVENT 
) 
2880         if ( m_inDoPropertyChanged 
) 
2886     wxPGProperty
* selected 
= GetSelection(); 
2889          IsEditorsValueModified() && 
2890          (m_iFlags 
& wxPG_FL_INITIALIZED
) && 
2893         m_inCommitChangesFromEditor 
= true; 
2895         wxVariant 
variant(selected
->GetValueRef()); 
2896         bool valueIsPending 
= false; 
2898         // JACS - necessary to avoid new focus being found spuriously within OnIdle 
2899         // due to another window getting focus 
2900         wxWindow
* oldFocus 
= m_curFocused
; 
2902         bool validationFailure 
= false; 
2903         bool forceSuccess 
= (flags 
& (wxPG_SEL_NOVALIDATE
|wxPG_SEL_FORCE
)) ? true : false; 
2905         m_chgInfo_changedProperty 
= NULL
; 
2907         // If truly modified, schedule value as pending. 
2908         if ( selected
->GetEditorClass()-> 
2909                 GetValueFromControl( variant
, 
2911                                      GetEditorControl() ) ) 
2913             if ( DoEditorValidate() && 
2914                  PerformValidation(selected
, variant
) ) 
2916                 valueIsPending 
= true; 
2920                 validationFailure 
= true; 
2925             EditorsValueWasNotModified(); 
2928         m_inCommitChangesFromEditor 
= false; 
2932         if ( validationFailure 
&& !forceSuccess 
) 
2936                 oldFocus
->SetFocus(); 
2937                 m_curFocused 
= oldFocus
; 
2940             res 
= OnValidationFailure(selected
, variant
); 
2942             // Now prevent further validation failure messages 
2945                 EditorsValueWasNotModified(); 
2946                 OnValidationFailureReset(selected
); 
2949         else if ( valueIsPending 
) 
2951             DoPropertyChanged( selected
, flags 
); 
2952             EditorsValueWasNotModified(); 
2961 // ----------------------------------------------------------------------- 
2963 bool wxPropertyGrid::PerformValidation( wxPGProperty
* p
, wxVariant
& pendingValue
, 
2967     // Runs all validation functionality. 
2968     // Returns true if value passes all tests. 
2971     m_validationInfo
.m_failureBehavior 
= m_permanentValidationFailureBehavior
; 
2972     m_validationInfo
.m_isFailing 
= true; 
2975     // Variant list a special value that cannot be validated 
2977     if ( pendingValue
.GetType() != wxPG_VARIANT_TYPE_LIST 
) 
2979         if ( !p
->ValidateValue(pendingValue
, m_validationInfo
) ) 
2984     // Adapt list to child values, if necessary 
2985     wxVariant listValue 
= pendingValue
; 
2986     wxVariant
* pPendingValue 
= &pendingValue
; 
2987     wxVariant
* pList 
= NULL
; 
2989     // If parent has wxPG_PROP_AGGREGATE flag, or uses composite 
2990     // string value, then we need treat as it was changed instead 
2991     // (or, in addition, as is the case with composite string parent). 
2992     // This includes creating list variant for child values. 
2994     wxPGProperty
* pwc 
= p
->GetParent(); 
2995     wxPGProperty
* changedProperty 
= p
; 
2996     wxPGProperty
* baseChangedProperty 
= changedProperty
; 
2997     wxVariant bcpPendingList
; 
2999     listValue 
= pendingValue
; 
3000     listValue
.SetName(p
->GetBaseName()); 
3003             (pwc
->HasFlag(wxPG_PROP_AGGREGATE
) || pwc
->HasFlag(wxPG_PROP_COMPOSED_VALUE
)) ) 
3005         wxVariantList tempList
; 
3006         wxVariant 
lv(tempList
, pwc
->GetBaseName()); 
3007         lv
.Append(listValue
); 
3009         pPendingValue 
= &listValue
; 
3011         if ( pwc
->HasFlag(wxPG_PROP_AGGREGATE
) ) 
3013             baseChangedProperty 
= pwc
; 
3014             bcpPendingList 
= lv
; 
3017         changedProperty 
= pwc
; 
3018         pwc 
= pwc
->GetParent(); 
3022     wxPGProperty
* evtChangingProperty 
= changedProperty
; 
3024     if ( pPendingValue
->GetType() != wxPG_VARIANT_TYPE_LIST 
) 
3026         value 
= *pPendingValue
; 
3030         // Convert list to child values 
3031         pList 
= pPendingValue
; 
3032         changedProperty
->AdaptListToValue( *pPendingValue
, &value 
); 
3035     wxVariant evtChangingValue 
= value
; 
3037     if ( flags 
& SendEvtChanging 
) 
3039         // FIXME: After proper ValueToString()s added, remove 
3040         // this. It is just a temporary fix, as evt_changing 
3041         // will simply not work for wxPG_PROP_COMPOSED_VALUE 
3042         // (unless it is selected, and textctrl editor is open). 
3043         if ( changedProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3045             evtChangingProperty 
= baseChangedProperty
; 
3046             if ( evtChangingProperty 
!= p 
) 
3048                 evtChangingProperty
->AdaptListToValue( bcpPendingList
, &evtChangingValue 
); 
3052                 evtChangingValue 
= pendingValue
; 
3056         if ( evtChangingProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3058             if ( changedProperty 
== GetSelection() ) 
3060                 wxWindow
* editor 
= GetEditorControl(); 
3061                 wxASSERT( editor
->IsKindOf(CLASSINFO(wxTextCtrl
)) ); 
3062                 evtChangingValue 
= wxStaticCast(editor
, wxTextCtrl
)->GetValue(); 
3066                 wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value.")); 
3071     wxASSERT( m_chgInfo_changedProperty 
== NULL 
); 
3072     m_chgInfo_changedProperty 
= changedProperty
; 
3073     m_chgInfo_baseChangedProperty 
= baseChangedProperty
; 
3074     m_chgInfo_pendingValue 
= value
; 
3077         m_chgInfo_valueList 
= *pList
; 
3079         m_chgInfo_valueList
.MakeNull(); 
3081     // If changedProperty is not property which value was edited, 
3082     // then call wxPGProperty::ValidateValue() for that as well. 
3083     if ( p 
!= changedProperty 
&& value
.GetType() != wxPG_VARIANT_TYPE_LIST 
) 
3085         if ( !changedProperty
->ValidateValue(value
, m_validationInfo
) ) 
3089     if ( flags 
& SendEvtChanging 
) 
3091         // SendEvent returns true if event was vetoed 
3092         if ( SendEvent( wxEVT_PG_CHANGING
, evtChangingProperty
, 
3093                         &evtChangingValue 
) ) 
3097     if ( flags 
& IsStandaloneValidation 
) 
3099         // If called in 'generic' context, we need to reset 
3100         // m_chgInfo_changedProperty and write back translated value. 
3101         m_chgInfo_changedProperty 
= NULL
; 
3102         pendingValue 
= value
; 
3105     m_validationInfo
.m_isFailing 
= false; 
3110 // ----------------------------------------------------------------------- 
3113 wxStatusBar
* wxPropertyGrid::GetStatusBar() 
3115     wxWindow
* topWnd 
= ::wxGetTopLevelParent(this); 
3116     if ( topWnd 
&& topWnd
->IsKindOf(CLASSINFO(wxFrame
)) ) 
3118         wxFrame
* pFrame 
= wxStaticCast(topWnd
, wxFrame
); 
3120             return pFrame
->GetStatusBar(); 
3126 // ----------------------------------------------------------------------- 
3128 void wxPropertyGrid::DoShowPropertyError( wxPGProperty
* WXUNUSED(property
), const wxString
& msg 
) 
3134     if ( !wxPGGlobalVars
->m_offline 
) 
3136         wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3139             pStatusBar
->SetStatusText(msg
); 
3145     ::wxMessageBox(msg
, _("Property Error")); 
3148 // ----------------------------------------------------------------------- 
3150 void wxPropertyGrid::DoHidePropertyError( wxPGProperty
* WXUNUSED(property
) ) 
3153     if ( !wxPGGlobalVars
->m_offline 
) 
3155         wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3158             pStatusBar
->SetStatusText(wxEmptyString
); 
3165 // ----------------------------------------------------------------------- 
3167 bool wxPropertyGrid::OnValidationFailure( wxPGProperty
* property
, 
3168                                           wxVariant
& invalidValue 
) 
3170     if ( m_inOnValidationFailure 
) 
3173     m_inOnValidationFailure 
= true; 
3174     wxON_BLOCK_EXIT_SET(m_inOnValidationFailure
, false); 
3176     wxWindow
* editor 
= GetEditorControl(); 
3177     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3179     if ( m_inDoSelectProperty 
) 
3181         // When property selection is being changed, do not display any 
3182         // messages, if some were already shown for this property. 
3183         if ( property
->HasFlag(wxPG_PROP_INVALID_VALUE
) ) 
3185             m_validationInfo
.m_failureBehavior 
= 
3186                 vfb 
& ~(wxPG_VFB_SHOW_MESSAGE 
| 
3187                         wxPG_VFB_SHOW_MESSAGEBOX 
| 
3188                         wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR
); 
3192     // First call property's handler 
3193     property
->OnValidationFailure(invalidValue
); 
3195     bool res 
= DoOnValidationFailure(property
, invalidValue
); 
3198     // For non-wxTextCtrl editors, we do need to revert the value 
3199     if ( !editor
->IsKindOf(CLASSINFO(wxTextCtrl
)) && 
3200          property 
== GetSelection() ) 
3202         property
->GetEditorClass()->UpdateControl(property
, editor
); 
3205     property
->SetFlag(wxPG_PROP_INVALID_VALUE
); 
3210 bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty
* property
, wxVariant
& WXUNUSED(invalidValue
) ) 
3212     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3214     if ( vfb 
& wxPG_VFB_BEEP 
) 
3217     if ( (vfb 
& wxPG_VFB_MARK_CELL
) && 
3218          !property
->HasFlag(wxPG_PROP_INVALID_VALUE
) ) 
3220         unsigned int colCount 
= m_pState
->GetColumnCount(); 
3222         // We need backup marked property's cells 
3223         m_propCellsBackup 
= property
->m_cells
; 
3225         wxColour vfbFg 
= *wxWHITE
; 
3226         wxColour vfbBg 
= *wxRED
; 
3228         property
->EnsureCells(colCount
); 
3230         for ( unsigned int i
=0; i
<colCount
; i
++ ) 
3232             wxPGCell
& cell 
= property
->m_cells
[i
]; 
3233             cell
.SetFgCol(vfbFg
); 
3234             cell
.SetBgCol(vfbBg
); 
3237         DrawItemAndChildren(property
); 
3239         if ( property 
== GetSelection() ) 
3241             SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
); 
3243             wxWindow
* editor 
= GetEditorControl(); 
3246                 editor
->SetForegroundColour(vfbFg
); 
3247                 editor
->SetBackgroundColour(vfbBg
); 
3252     if ( vfb 
& (wxPG_VFB_SHOW_MESSAGE 
| 
3253                 wxPG_VFB_SHOW_MESSAGEBOX 
| 
3254                 wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR
) ) 
3256         wxString msg 
= m_validationInfo
.m_failureMessage
; 
3259             msg 
= _("You have entered invalid value. Press ESC to cancel editing."); 
3262         if ( vfb 
& wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR 
) 
3264             if ( !wxPGGlobalVars
->m_offline 
) 
3266                 wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3268                     pStatusBar
->SetStatusText(msg
); 
3273         if ( vfb 
& wxPG_VFB_SHOW_MESSAGE 
) 
3274             DoShowPropertyError(property
, msg
); 
3276         if ( vfb 
& wxPG_VFB_SHOW_MESSAGEBOX 
) 
3277             ::wxMessageBox(msg
, _("Property Error")); 
3280     return (vfb 
& wxPG_VFB_STAY_IN_PROPERTY
) ? false : true; 
3283 // ----------------------------------------------------------------------- 
3285 void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty
* property 
) 
3287     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3289     if ( vfb 
& wxPG_VFB_MARK_CELL 
) 
3292         property
->m_cells 
= m_propCellsBackup
; 
3294         ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
); 
3296         if ( property 
== GetSelection() && GetEditorControl() ) 
3298             // Calling this will recreate the control, thus resetting its colour 
3299             RefreshProperty(property
); 
3303             DrawItemAndChildren(property
); 
3308     if ( vfb 
& wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR 
) 
3310         if ( !wxPGGlobalVars
->m_offline 
) 
3312             wxStatusBar
* pStatusBar 
= GetStatusBar(); 
3314                 pStatusBar
->SetStatusText(wxEmptyString
); 
3319     if ( vfb 
& wxPG_VFB_SHOW_MESSAGE 
) 
3321         DoHidePropertyError(property
); 
3324     m_validationInfo
.m_isFailing 
= false; 
3327 // ----------------------------------------------------------------------- 
3329 // flags are same as with DoSelectProperty 
3330 bool wxPropertyGrid::DoPropertyChanged( wxPGProperty
* p
, unsigned int selFlags 
) 
3332     if ( m_inDoPropertyChanged 
) 
3335     m_inDoPropertyChanged 
= true; 
3336     wxON_BLOCK_EXIT_SET(m_inDoPropertyChanged
, false); 
3338     wxPGProperty
* selected 
= GetSelection(); 
3340     m_pState
->m_anyModified 
= 1; 
3342     // If property's value is being changed, assume it is valid 
3343     OnValidationFailureReset(selected
); 
3345     // Maybe need to update control 
3346     wxASSERT( m_chgInfo_changedProperty 
!= NULL 
); 
3348     // These values were calculated in PerformValidation() 
3349     wxPGProperty
* changedProperty 
= m_chgInfo_changedProperty
; 
3350     wxVariant value 
= m_chgInfo_pendingValue
; 
3352     wxPGProperty
* topPaintedProperty 
= changedProperty
; 
3354     while ( !topPaintedProperty
->IsCategory() && 
3355             !topPaintedProperty
->IsRoot() ) 
3357         topPaintedProperty 
= topPaintedProperty
->GetParent(); 
3360     changedProperty
->SetValue(value
, &m_chgInfo_valueList
, wxPG_SETVAL_BY_USER
); 
3362     // NB: Call GetEditorControl() as late as possible, because OnSetValue() 
3363     //     and perhaps other user-defined virtual functions may change it. 
3364     wxWindow
* editor 
= GetEditorControl(); 
3366     // Set as Modified (not if dragging just began) 
3367     if ( !(p
->m_flags 
& wxPG_PROP_MODIFIED
) ) 
3369         p
->m_flags 
|= wxPG_PROP_MODIFIED
; 
3370         if ( p 
== selected 
&& (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3373                 SetCurControlBoldFont(); 
3379     // Propagate updates to parent(s) 
3381     wxPGProperty
* prevPwc 
= NULL
; 
3383     while ( prevPwc 
!= topPaintedProperty 
) 
3385         pwc
->m_flags 
|= wxPG_PROP_MODIFIED
; 
3387         if ( pwc 
== selected 
&& (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3390                 SetCurControlBoldFont(); 
3394         pwc 
= pwc
->GetParent(); 
3397     // Draw the actual property 
3398     DrawItemAndChildren( topPaintedProperty 
); 
3401     // If value was set by wxPGProperty::OnEvent, then update the editor 
3403     if ( selFlags 
& wxPG_SEL_DIALOGVAL 
) 
3409 #if wxPG_REFRESH_CONTROLS 
3410         if ( m_wndEditor 
) m_wndEditor
->Refresh(); 
3411         if ( m_wndEditor2 
) m_wndEditor2
->Refresh(); 
3416     wxASSERT( !changedProperty
->GetParent()->HasFlag(wxPG_PROP_AGGREGATE
) ); 
3418     // If top parent has composite string value, then send to child parents, 
3419     // starting from baseChangedProperty. 
3420     if ( changedProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3422         pwc 
= m_chgInfo_baseChangedProperty
; 
3424         while ( pwc 
!= changedProperty 
) 
3426             SendEvent( wxEVT_PG_CHANGED
, pwc
, NULL 
); 
3427             pwc 
= pwc
->GetParent(); 
3431     SendEvent( wxEVT_PG_CHANGED
, changedProperty
, NULL 
); 
3436 // ----------------------------------------------------------------------- 
3438 bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id
, wxVariant newValue 
) 
3440     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
3442     m_chgInfo_changedProperty 
= NULL
; 
3444     if ( PerformValidation(p
, newValue
) ) 
3446         DoPropertyChanged(p
); 
3451         OnValidationFailure(p
, newValue
); 
3457 // ----------------------------------------------------------------------- 
3459 wxVariant 
wxPropertyGrid::GetUncommittedPropertyValue() 
3461     wxPGProperty
* prop 
= GetSelectedProperty(); 
3464         return wxNullVariant
; 
3466     wxTextCtrl
* tc 
= GetEditorTextCtrl(); 
3467     wxVariant value 
= prop
->GetValue(); 
3469     if ( !tc 
|| !IsEditorsValueModified() ) 
3472     if ( !prop
->StringToValue(value
, tc
->GetValue()) ) 
3475     if ( !PerformValidation(prop
, value
, IsStandaloneValidation
) ) 
3476         return prop
->GetValue(); 
3481 // ----------------------------------------------------------------------- 
3483 // Runs wxValidator for the selected property 
3484 bool wxPropertyGrid::DoEditorValidate() 
3486 #if wxUSE_VALIDATORS 
3487     wxRecursionGuard 
guard(m_validatingEditor
); 
3488     if ( guard
.IsInside() ) 
3491     wxPGProperty
* selected 
= GetSelection(); 
3494         wxWindow
* wnd 
= GetEditorControl(); 
3496         wxValidator
* validator 
= selected
->GetValidator(); 
3497         if ( validator 
&& wnd 
) 
3499             validator
->SetWindow(wnd
); 
3500             if ( !validator
->Validate(this) ) 
3508 // ----------------------------------------------------------------------- 
3510 bool wxPropertyGrid::HandleCustomEditorEvent( wxEvent 
&event 
) 
3513     // NB: We should return true if the event was recognized as 
3514     //     a dedicated wxPropertyGrid event, and as such was 
3515     //     either properly handled or ignored. 
3518     // It is possible that this handler receives event even before 
3519     // the control has been properly initialized. Let's skip the 
3520     // event handling in that case. 
3524     // Don't care about the event if it originated from the 
3525     // 'label editor'. In this function we only care about the 
3526     // property value editor. 
3527     if ( m_labelEditor 
&& event
.GetId() == m_labelEditor
->GetId() ) 
3533     wxPGProperty
* selected 
= GetSelection(); 
3535     // Somehow, event is handled after property has been deselected. 
3536     // Possibly, but very rare. 
3538           selected
->HasFlag(wxPG_PROP_BEING_DELETED
) || 
3539           m_inOnValidationFailure 
|| 
3540           // Also don't handle editor event if wxEVT_PG_CHANGED or 
3541           // similar is currently doing something (showing a 
3542           // message box, for instance). 
3546     if ( m_iFlags 
& wxPG_FL_IN_HANDLECUSTOMEDITOREVENT 
) 
3549     wxVariant 
pendingValue(selected
->GetValueRef()); 
3550     wxWindow
* wnd 
= GetEditorControl(); 
3551     wxWindow
* editorWnd 
= wxDynamicCast(event
.GetEventObject(), wxWindow
); 
3553     bool wasUnspecified 
= selected
->IsValueUnspecified(); 
3554     int usesAutoUnspecified 
= selected
->UsesAutoUnspecified(); 
3555     bool valueIsPending 
= false; 
3557     m_chgInfo_changedProperty 
= NULL
; 
3559     m_iFlags 
&= ~wxPG_FL_VALUE_CHANGE_IN_EVENT
; 
3562     // Filter out excess wxTextCtrl modified events 
3563     if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED 
&& wnd 
) 
3565         if ( wnd
->IsKindOf(CLASSINFO(wxTextCtrl
)) ) 
3567             wxTextCtrl
* tc 
= (wxTextCtrl
*) wnd
; 
3569             wxString newTcValue 
= tc
->GetValue(); 
3570             if ( m_prevTcValue 
== newTcValue 
) 
3572             m_prevTcValue 
= newTcValue
; 
3574         else if ( wnd
->IsKindOf(CLASSINFO(wxComboCtrl
)) ) 
3576             // In some cases we might stumble unintentionally on 
3577             // wxComboCtrl's embedded wxTextCtrl's events. Let's 
3579             if ( editorWnd
->IsKindOf(CLASSINFO(wxTextCtrl
)) ) 
3582             wxComboCtrl
* cc 
= (wxComboCtrl
*) wnd
; 
3584             wxString newTcValue 
= cc
->GetTextCtrl()->GetValue(); 
3585             if ( m_prevTcValue 
== newTcValue 
) 
3587             m_prevTcValue 
= newTcValue
; 
3591     SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
); 
3593     bool validationFailure 
= false; 
3594     bool buttonWasHandled 
= false; 
3595     bool result 
= false; 
3598     // Try common button handling 
3599     if ( m_wndEditor2 
&& event
.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED 
) 
3601         wxPGEditorDialogAdapter
* adapter 
= selected
->GetEditorDialog(); 
3605             buttonWasHandled 
= true; 
3606             // Store as res2, as previously (and still currently alternatively) 
3607             // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED 
3608             // in wxPGProperty::OnEvent(). 
3609             adapter
->ShowDialog( this, selected 
); 
3614     if ( !buttonWasHandled 
) 
3616         if ( wnd 
|| m_wndEditor2 
) 
3618             // First call editor class' event handler. 
3619             const wxPGEditor
* editor 
= selected
->GetEditorClass(); 
3621             if ( editor
->OnEvent( this, selected
, editorWnd
, event 
) ) 
3625                 // If changes, validate them 
3626                 if ( DoEditorValidate() ) 
3628                     if ( editor
->GetValueFromControl( pendingValue
, 
3631                         valueIsPending 
= true; 
3633                     // Mark value always as pending if validation is currently 
3634                     // failing and value was not unspecified 
3635                     if ( !valueIsPending 
&& 
3636                          !pendingValue
.IsNull() && 
3637                          m_validationInfo
.m_isFailing 
) 
3638                          valueIsPending 
= true; 
3642                     validationFailure 
= true; 
3647         // Then the property's custom handler (must be always called, unless 
3648         // validation failed). 
3649         if ( !validationFailure 
) 
3650             buttonWasHandled 
= selected
->OnEvent( this, editorWnd
, event 
); 
3653     // SetValueInEvent(), as called in one of the functions referred above 
3654     // overrides editor's value. 
3655     if ( m_iFlags 
& wxPG_FL_VALUE_CHANGE_IN_EVENT 
) 
3657         valueIsPending 
= true; 
3658         pendingValue 
= m_changeInEventValue
; 
3659         selFlags 
|= wxPG_SEL_DIALOGVAL
; 
3662     if ( !validationFailure 
&& valueIsPending 
) 
3663         if ( !PerformValidation(selected
, pendingValue
) ) 
3664             validationFailure 
= true; 
3666     if ( validationFailure
) 
3668         OnValidationFailure(selected
, pendingValue
); 
3670     else if ( valueIsPending 
) 
3672         selFlags 
|= ( !wasUnspecified 
&& selected
->IsValueUnspecified() && usesAutoUnspecified 
) ? wxPG_SEL_SETUNSPEC 
: 0; 
3674         DoPropertyChanged(selected
, selFlags
); 
3675         EditorsValueWasNotModified(); 
3677         // Regardless of editor type, unfocus editor on 
3678         // text-editing related enter press. 
3679         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3686         // No value after all 
3688         // Regardless of editor type, unfocus editor on 
3689         // text-editing related enter press. 
3690         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3695         // Let unhandled button click events go to the parent 
3696         if ( !buttonWasHandled 
&& event
.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED 
) 
3699             wxCommandEvent 
evt(wxEVT_COMMAND_BUTTON_CLICKED
,GetId()); 
3700             GetEventHandler()->AddPendingEvent(evt
); 
3704     ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
); 
3709 // ----------------------------------------------------------------------- 
3710 // wxPropertyGrid editor control helper methods 
3711 // ----------------------------------------------------------------------- 
3713 wxRect 
wxPropertyGrid::GetEditorWidgetRect( wxPGProperty
* p
, int column 
) const 
3715     int itemy 
= p
->GetY2(m_lineHeight
); 
3716     int splitterX 
= m_pState
->DoGetSplitterPosition(column
-1); 
3717     int colEnd 
= splitterX 
+ m_pState
->m_colWidths
[column
]; 
3718     int imageOffset 
= 0; 
3720     int vx
, vy
;  // Top left corner of client 
3721     GetViewStart(&vx
, &vy
); 
3722     vy 
*= wxPG_PIXELS_PER_UNIT
; 
3726         // TODO: If custom image detection changes from current, change this. 
3727         if ( m_iFlags 
& wxPG_FL_CUR_USES_CUSTOM_IMAGE 
) 
3729             //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; 
3730             int iw 
= p
->OnMeasureImage().x
; 
3732                 iw 
= wxPG_CUSTOM_IMAGE_WIDTH
; 
3733             imageOffset 
= p
->GetImageOffset(iw
); 
3736     else if ( column 
== 0 ) 
3738         splitterX 
+= (p
->m_depth 
- 1) * m_subgroup_extramargin
; 
3743         splitterX
+imageOffset
+wxPG_XBEFOREWIDGET
+wxPG_CONTROL_MARGIN
+1, 
3745         colEnd
-splitterX
-wxPG_XBEFOREWIDGET
-wxPG_CONTROL_MARGIN
-imageOffset
-1, 
3750 // ----------------------------------------------------------------------- 
3752 wxRect 
wxPropertyGrid::GetImageRect( wxPGProperty
* p
, int item 
) const 
3754     wxSize sz 
= GetImageSize(p
, item
); 
3755     return wxRect(wxPG_CONTROL_MARGIN 
+ wxCC_CUSTOM_IMAGE_MARGIN1
, 
3756                   wxPG_CUSTOM_IMAGE_SPACINGY
, 
3761 // return size of custom paint image 
3762 wxSize 
wxPropertyGrid::GetImageSize( wxPGProperty
* p
, int item 
) const 
3764     // If called with NULL property, then return default image 
3765     // size for properties that use image. 
3767         return wxSize(wxPG_CUSTOM_IMAGE_WIDTH
,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
)); 
3769     wxSize cis 
= p
->OnMeasureImage(item
); 
3771     int choiceCount 
= p
->m_choices
.GetCount(); 
3772     int comVals 
= p
->GetDisplayedCommonValueCount(); 
3773     if ( item 
>= choiceCount 
&& comVals 
> 0 ) 
3775         unsigned int cvi 
= item
-choiceCount
; 
3776         cis 
= GetCommonValue(cvi
)->GetRenderer()->GetImageSize(NULL
, 1, cvi
); 
3778     else if ( item 
>= 0 && choiceCount 
== 0 ) 
3779         return wxSize(0, 0); 
3784             cis
.x 
= wxPG_CUSTOM_IMAGE_WIDTH
; 
3789             cis
.y 
= wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
); 
3796 // ----------------------------------------------------------------------- 
3798 // takes scrolling into account 
3799 void wxPropertyGrid::ImprovedClientToScreen( int* px
, int* py 
) 
3802     GetViewStart(&vx
,&vy
); 
3803     vy
*=wxPG_PIXELS_PER_UNIT
; 
3804     vx
*=wxPG_PIXELS_PER_UNIT
; 
3807     ClientToScreen( px
, py 
); 
3810 // ----------------------------------------------------------------------- 
3812 wxPropertyGridHitTestResult 
wxPropertyGrid::HitTest( const wxPoint
& pt 
) const 
3815     GetViewStart(&pt2
.x
,&pt2
.y
); 
3816     pt2
.x 
*= wxPG_PIXELS_PER_UNIT
; 
3817     pt2
.y 
*= wxPG_PIXELS_PER_UNIT
; 
3821     return m_pState
->HitTest(pt2
); 
3824 // ----------------------------------------------------------------------- 
3826 // custom set cursor 
3827 void wxPropertyGrid::CustomSetCursor( int type
, bool override 
) 
3829     if ( type 
== m_curcursor 
&& !override 
) return; 
3831     wxCursor
* cursor 
= &wxPG_DEFAULT_CURSOR
; 
3833     if ( type 
== wxCURSOR_SIZEWE 
) 
3834         cursor 
= m_cursorSizeWE
; 
3836     SetCursor( *cursor 
); 
3841 // ----------------------------------------------------------------------- 
3844 wxPropertyGrid::GetUnspecifiedValueText( int argFlags 
) const 
3846     const wxPGCell
& ua 
= GetUnspecifiedValueAppearance(); 
3848     if ( ua
.HasText() && 
3849          !(argFlags 
& wxPG_FULL_VALUE
) && 
3850          !(argFlags 
& wxPG_EDITABLE_VALUE
) ) 
3851         return ua
.GetText(); 
3853     return wxEmptyString
; 
3856 // ----------------------------------------------------------------------- 
3857 // wxPropertyGrid property selection, editor creation 
3858 // ----------------------------------------------------------------------- 
3861 // This class forwards events from property editor controls to wxPropertyGrid. 
3862 class wxPropertyGridEditorEventForwarder 
: public wxEvtHandler
 
3865     wxPropertyGridEditorEventForwarder( wxPropertyGrid
* propGrid 
) 
3866         : wxEvtHandler(), m_propGrid(propGrid
) 
3870     virtual ~wxPropertyGridEditorEventForwarder() 
3875     bool ProcessEvent( wxEvent
& event 
) 
3880         m_propGrid
->HandleCustomEditorEvent(event
); 
3883         // NB: We should return true if the event was recognized as 
3884         //     a dedicated wxPropertyGrid event, and as such was 
3885         //     either properly handled or ignored. 
3887         if ( m_propGrid
->IsMainButtonEvent(event
) ) 
3891         // NB: On wxMSW, a wxTextCtrl with wxTE_PROCESS_ENTER 
3892         //     may beep annoyingly if that event is skipped 
3893         //     and passed to parent event handler. 
3894         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3897         return wxEvtHandler::ProcessEvent(event
); 
3900     wxPropertyGrid
*         m_propGrid
; 
3903 // Setups event handling for child control 
3904 void wxPropertyGrid::SetupChildEventHandling( wxWindow
* argWnd 
) 
3906     wxWindowID id 
= argWnd
->GetId(); 
3908     if ( argWnd 
== m_wndEditor 
) 
3910         argWnd
->Connect(id
, wxEVT_MOTION
, 
3911             wxMouseEventHandler(wxPropertyGrid::OnMouseMoveChild
), 
3913         argWnd
->Connect(id
, wxEVT_LEFT_UP
, 
3914             wxMouseEventHandler(wxPropertyGrid::OnMouseUpChild
), 
3916         argWnd
->Connect(id
, wxEVT_LEFT_DOWN
, 
3917             wxMouseEventHandler(wxPropertyGrid::OnMouseClickChild
), 
3919         argWnd
->Connect(id
, wxEVT_RIGHT_UP
, 
3920             wxMouseEventHandler(wxPropertyGrid::OnMouseRightClickChild
), 
3922         argWnd
->Connect(id
, wxEVT_ENTER_WINDOW
, 
3923             wxMouseEventHandler(wxPropertyGrid::OnMouseEntry
), 
3925         argWnd
->Connect(id
, wxEVT_LEAVE_WINDOW
, 
3926             wxMouseEventHandler(wxPropertyGrid::OnMouseEntry
), 
3930     wxPropertyGridEditorEventForwarder
* forwarder
; 
3931     forwarder 
= new wxPropertyGridEditorEventForwarder(this); 
3932     argWnd
->PushEventHandler(forwarder
); 
3934     argWnd
->Connect(id
, wxEVT_KEY_DOWN
, 
3935         wxCharEventHandler(wxPropertyGrid::OnChildKeyDown
), 
3939 void wxPropertyGrid::DestroyEditorWnd( wxWindow
* wnd 
) 
3946     // Do not free editors immediately (for sake of processing events) 
3947     wxPendingDelete
.Append(wnd
); 
3950 void wxPropertyGrid::FreeEditors() 
3953     // Return focus back to canvas from children (this is required at least for 
3954     // GTK+, which, unlike Windows, clears focus when control is destroyed 
3955     // instead of moving it to closest parent). 
3958     // Do not free editors immediately if processing events 
3961         wxEvtHandler
* handler 
= m_wndEditor2
->PopEventHandler(false); 
3962         m_wndEditor2
->Hide(); 
3963         wxPendingDelete
.Append( handler 
); 
3964         DestroyEditorWnd(m_wndEditor2
); 
3965         m_wndEditor2 
= NULL
; 
3970         wxEvtHandler
* handler 
= m_wndEditor
->PopEventHandler(false); 
3971         m_wndEditor
->Hide(); 
3972         wxPendingDelete
.Append( handler 
); 
3973         DestroyEditorWnd(m_wndEditor
); 
3978 // Call with NULL to de-select property 
3979 bool wxPropertyGrid::DoSelectProperty( wxPGProperty
* p
, unsigned int flags 
) 
3984         wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(), 
3985             p->m_parent->m_label.c_str(),p->GetIndexInParent()); 
3989         wxLogDebug(wxT("SelectProperty( NULL, -1 )")); 
3993     if ( m_inDoSelectProperty 
) 
3996     m_inDoSelectProperty 
= true; 
3997     wxON_BLOCK_EXIT_SET(m_inDoSelectProperty
, false); 
4002     wxArrayPGProperty prevSelection 
= m_pState
->m_selection
; 
4003     wxPGProperty
* prevFirstSel
; 
4005     if ( prevSelection
.size() > 0 ) 
4006         prevFirstSel 
= prevSelection
[0]; 
4008         prevFirstSel 
= NULL
; 
4010     if ( prevFirstSel 
&& prevFirstSel
->HasFlag(wxPG_PROP_BEING_DELETED
) ) 
4011         prevFirstSel 
= NULL
; 
4013     // Always send event, as this is indirect call 
4014     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
); 
4018         wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() ); 
4020         wxPrintf( "None selected\n" ); 
4023         wxPrintf( "P =  %s\n", p->GetClassInfo()->GetClassName() ); 
4025         wxPrintf( "P = NULL\n" ); 
4028     wxWindow
* primaryCtrl 
= NULL
; 
4030     // If we are frozen, then just set the values. 
4033         m_iFlags 
&= ~(wxPG_FL_ABNORMAL_EDITOR
); 
4034         m_editorFocused 
= 0; 
4035         m_pState
->DoSetSelection(p
); 
4037         // If frozen, always free controls. But don't worry, as Thaw will 
4038         // recall SelectProperty to recreate them. 
4041         // Prevent any further selection measures in this call 
4047         if ( prevFirstSel 
== p 
&& 
4048              prevSelection
.size() <= 1 && 
4049              !(flags 
& wxPG_SEL_FORCE
) ) 
4051             // Only set focus if not deselecting 
4054                 if ( flags 
& wxPG_SEL_FOCUS 
) 
4058                         m_wndEditor
->SetFocus(); 
4059                         m_editorFocused 
= 1; 
4072         // First, deactivate previous 
4075             // Must double-check if this is an selected in case of forceswitch 
4076             if ( p 
!= prevFirstSel 
) 
4078                 if ( !CommitChangesFromEditor(flags
) ) 
4080                     // Validation has failed, so we can't exit the previous editor 
4081                     //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."), 
4082                     //               _("Invalid Value"),wxOK|wxICON_ERROR); 
4087             // This should be called after CommitChangesFromEditor(), so that 
4088             // OnValidationFailure() still has information on property's 
4089             // validation state. 
4090             OnValidationFailureReset(prevFirstSel
); 
4094             m_iFlags 
&= ~(wxPG_FL_ABNORMAL_EDITOR
); 
4095             EditorsValueWasNotModified(); 
4098         SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY
); 
4100         m_pState
->DoSetSelection(p
); 
4102         // Redraw unselected 
4103         for ( unsigned int i
=0; i
<prevSelection
.size(); i
++ ) 
4105             DrawItem(prevSelection
[i
]); 
4109         // Then, activate the one given. 
4112             int propY 
= p
->GetY2(m_lineHeight
); 
4114             int splitterX 
= GetSplitterPosition(); 
4115             m_editorFocused 
= 0; 
4116             m_iFlags 
|= wxPG_FL_PRIMARY_FILLS_ENTIRE
; 
4118             wxASSERT( m_wndEditor 
== NULL 
); 
4121             // Only create editor for non-disabled non-caption 
4122             if ( !p
->IsCategory() && !(p
->m_flags 
& wxPG_PROP_DISABLED
) ) 
4124             // do this for non-caption items 
4128                 // Do we need to paint the custom image, if any? 
4129                 m_iFlags 
&= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE
); 
4130                 if ( (p
->m_flags 
& wxPG_PROP_CUSTOMIMAGE
) && 
4131                      !p
->GetEditorClass()->CanContainCustomImage() 
4133                     m_iFlags 
|= wxPG_FL_CUR_USES_CUSTOM_IMAGE
; 
4135                 wxRect grect 
= GetEditorWidgetRect(p
, m_selColumn
); 
4136                 wxPoint goodPos 
= grect
.GetPosition(); 
4138                 // Editor appearance can now be considered clear 
4139                 m_editorAppearance
.SetEmptyData(); 
4141                 const wxPGEditor
* editor 
= p
->GetEditorClass(); 
4142                 wxCHECK_MSG(editor
, false, 
4143                     wxT("NULL editor class not allowed")); 
4145                 m_iFlags 
&= ~wxPG_FL_FIXED_WIDTH_EDITOR
; 
4147                 wxPGWindowList wndList 
= 
4148                     editor
->CreateControls(this, 
4153                 m_wndEditor 
= wndList
.m_primary
; 
4154                 m_wndEditor2 
= wndList
.m_secondary
; 
4155                 primaryCtrl 
= GetEditorControl(); 
4158                 // Essentially, primaryCtrl == m_wndEditor 
4161                 // NOTE: It is allowed for m_wndEditor to be NULL - in this 
4162                 //       case value is drawn as normal, and m_wndEditor2 is 
4163                 //       assumed to be a right-aligned button that triggers 
4164                 //       a separate editorCtrl window. 
4168                     wxASSERT_MSG( m_wndEditor
->GetParent() == GetPanel(), 
4169                                   "CreateControls must use result of " 
4170                                   "wxPropertyGrid::GetPanel() as parent " 
4173                     // Set validator, if any 
4174                 #if wxUSE_VALIDATORS 
4175                     wxValidator
* validator 
= p
->GetValidator(); 
4177                         primaryCtrl
->SetValidator(*validator
); 
4180                     if ( m_wndEditor
->GetSize().y 
> (m_lineHeight
+6) ) 
4181                         m_iFlags 
|= wxPG_FL_ABNORMAL_EDITOR
; 
4183                     // If it has modified status, use bold font 
4184                     // (must be done before capturing m_ctrlXAdjust) 
4185                     if ( (p
->m_flags 
& wxPG_PROP_MODIFIED
) && 
4186                          (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
4187                         SetCurControlBoldFont(); 
4189                     // Store x relative to splitter (we'll need it). 
4190                     m_ctrlXAdjust 
= m_wndEditor
->GetPosition().x 
- splitterX
; 
4192                     // Check if background clear is not necessary 
4193                     wxPoint pos 
= m_wndEditor
->GetPosition(); 
4194                     if ( pos
.x 
> (splitterX
+1) || pos
.y 
> propY 
) 
4196                         m_iFlags 
&= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE
); 
4199                     m_wndEditor
->SetSizeHints(3, 3); 
4201                     SetupChildEventHandling(primaryCtrl
); 
4203                     // Focus and select all (wxTextCtrl, wxComboBox etc) 
4204                     if ( flags 
& wxPG_SEL_FOCUS 
) 
4206                         primaryCtrl
->SetFocus(); 
4208                         p
->GetEditorClass()->OnFocus(p
, primaryCtrl
); 
4212                         if ( p
->IsValueUnspecified() ) 
4213                             SetEditorAppearance(m_unspecifiedAppearance
, 
4220                     wxASSERT_MSG( m_wndEditor2
->GetParent() == GetPanel(), 
4221                                   "CreateControls must use result of " 
4222                                   "wxPropertyGrid::GetPanel() as parent " 
4225                     // Get proper id for wndSecondary 
4226                     m_wndSecId 
= m_wndEditor2
->GetId(); 
4227                     wxWindowList children 
= m_wndEditor2
->GetChildren(); 
4228                     wxWindowList::iterator node 
= children
.begin(); 
4229                     if ( node 
!= children
.end() ) 
4230                         m_wndSecId 
= ((wxWindow
*)*node
)->GetId(); 
4232                     m_wndEditor2
->SetSizeHints(3,3); 
4234                     m_wndEditor2
->Show(); 
4236                     SetupChildEventHandling(m_wndEditor2
); 
4238                     // If no primary editor, focus to button to allow 
4239                     // it to interprete ENTER etc. 
4240                     // NOTE: Due to problems focusing away from it, this 
4241                     //       has been disabled. 
4243                     if ( (flags & wxPG_SEL_FOCUS) && !m_wndEditor ) 
4244                         m_wndEditor2->SetFocus(); 
4248                 if ( flags 
& wxPG_SEL_FOCUS 
) 
4249                     m_editorFocused 
= 1; 
4254                 // Make sure focus is in grid canvas (important for wxGTK, 
4259             EditorsValueWasNotModified(); 
4261             // If it's inside collapsed section, expand parent, scroll, etc. 
4262             // Also, if it was partially visible, scroll it into view. 
4263             if ( !(flags 
& wxPG_SEL_NONVISIBLE
) ) 
4268                 m_wndEditor
->Show(true); 
4271             if ( !(flags 
& wxPG_SEL_NO_REFRESH
) ) 
4276             // Make sure focus is in grid canvas 
4280         ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY
); 
4283     const wxString
* pHelpString 
= NULL
; 
4286         pHelpString 
= &p
->GetHelpString(); 
4288     if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS
) ) 
4293         // Show help text in status bar. 
4294         //   (if found and grid not embedded in manager with help box and 
4295         //    style wxPG_EX_HELP_AS_TOOLTIPS is not used). 
4297         wxStatusBar
* statusbar 
= GetStatusBar(); 
4300             if ( pHelpString 
&& !pHelpString
->empty() ) 
4302                 // Set help box text. 
4303                 statusbar
->SetStatusText( *pHelpString 
); 
4304                 m_iFlags 
|= wxPG_FL_STRING_IN_STATUSBAR
; 
4306             else if ( m_iFlags 
& wxPG_FL_STRING_IN_STATUSBAR 
) 
4308                 // Clear help box - but only if it was written 
4309                 // by us at previous time. 
4310                 statusbar
->SetStatusText( m_emptyString 
); 
4311                 m_iFlags 
&= ~(wxPG_FL_STRING_IN_STATUSBAR
); 
4318 #if wxPG_SUPPORT_TOOLTIPS 
4320         // Show help as a tool tip on the editor control. 
4322         if ( pHelpString 
&& !pHelpString
->empty() && 
4325             primaryCtrl
->SetToolTip(*pHelpString
); 
4330     // call wx event handler (here so that it also occurs on deselection) 
4331     if ( !(flags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
4332         SendEvent( wxEVT_PG_SELECTED
, p
, NULL 
); 
4337 // ----------------------------------------------------------------------- 
4339 bool wxPropertyGrid::UnfocusEditor() 
4341     wxPGProperty
* selected 
= GetSelection(); 
4343     if ( !selected 
|| !m_wndEditor 
|| m_frozen 
) 
4346     if ( !CommitChangesFromEditor(0) ) 
4355 // ----------------------------------------------------------------------- 
4357 void wxPropertyGrid::RefreshEditor() 
4359     wxPGProperty
* p 
= GetSelection(); 
4363     wxWindow
* wnd 
= GetEditorControl(); 
4367     // Set editor font boldness - must do this before 
4368     // calling UpdateControl(). 
4369     if ( HasFlag(wxPG_BOLD_MODIFIED
) ) 
4371         if ( p
->HasFlag(wxPG_PROP_MODIFIED
) ) 
4372             wnd
->SetFont(GetCaptionFont()); 
4374             wnd
->SetFont(GetFont()); 
4377     const wxPGEditor
* editorClass 
= p
->GetEditorClass(); 
4379     editorClass
->UpdateControl(p
, wnd
); 
4381     if ( p
->IsValueUnspecified() ) 
4382         SetEditorAppearance(m_unspecifiedAppearance
, true); 
4385 // ----------------------------------------------------------------------- 
4387 bool wxPropertyGrid::SelectProperty( wxPGPropArg id
, bool focus 
) 
4389     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
4391     int flags 
= wxPG_SEL_DONT_SEND_EVENT
; 
4393         flags 
|= wxPG_SEL_FOCUS
; 
4395     return DoSelectProperty(p
, flags
); 
4398 // ----------------------------------------------------------------------- 
4399 // wxPropertyGrid expand/collapse state 
4400 // ----------------------------------------------------------------------- 
4402 bool wxPropertyGrid::DoCollapse( wxPGProperty
* p
, bool sendEvents 
) 
4404     wxPGProperty
* pwc 
= wxStaticCast(p
, wxPGProperty
); 
4405     wxPGProperty
* selected 
= GetSelection(); 
4407     // If active editor was inside collapsed section, then disable it 
4408     if ( selected 
&& selected
->IsSomeParent(p
) ) 
4413     // Store dont-center-splitter flag 'cause we need to temporarily set it 
4414     bool prevDontCenterSplitter 
= m_pState
->m_dontCenterSplitter
; 
4415     m_pState
->m_dontCenterSplitter 
= true; 
4417     bool res 
= m_pState
->DoCollapse(pwc
); 
4422             SendEvent( wxEVT_PG_ITEM_COLLAPSED
, p 
); 
4424         RecalculateVirtualSize(); 
4428     m_pState
->m_dontCenterSplitter 
= prevDontCenterSplitter
; 
4433 // ----------------------------------------------------------------------- 
4435 bool wxPropertyGrid::DoExpand( wxPGProperty
* p
, bool sendEvents 
) 
4437     wxCHECK_MSG( p
, false, wxT("invalid property id") ); 
4439     wxPGProperty
* pwc 
= (wxPGProperty
*)p
; 
4441     // Store dont-center-splitter flag 'cause we need to temporarily set it 
4442     bool prevDontCenterSplitter 
= m_pState
->m_dontCenterSplitter
; 
4443     m_pState
->m_dontCenterSplitter 
= true; 
4445     bool res 
= m_pState
->DoExpand(pwc
); 
4450             SendEvent( wxEVT_PG_ITEM_EXPANDED
, p 
); 
4452         RecalculateVirtualSize(); 
4456     m_pState
->m_dontCenterSplitter 
= prevDontCenterSplitter
; 
4461 // ----------------------------------------------------------------------- 
4463 bool wxPropertyGrid::DoHideProperty( wxPGProperty
* p
, bool hide
, int flags 
) 
4466         return m_pState
->DoHideProperty(p
, hide
, flags
); 
4468     wxArrayPGProperty selection 
= m_pState
->m_selection
;  // Must use a copy 
4469     int selRemoveCount 
= 0; 
4470     for ( unsigned int i
=0; i
<selection
.size(); i
++ ) 
4472         wxPGProperty
* selected 
= selection
[i
]; 
4473         if ( selected 
== p 
|| selected
->IsSomeParent(p
) ) 
4475             if ( !DoRemoveFromSelection(p
, flags
) ) 
4477             selRemoveCount 
+= 1; 
4481     m_pState
->DoHideProperty(p
, hide
, flags
); 
4483     RecalculateVirtualSize(); 
4490 // ----------------------------------------------------------------------- 
4491 // wxPropertyGrid size related methods 
4492 // ----------------------------------------------------------------------- 
4494 void wxPropertyGrid::RecalculateVirtualSize( int forceXPos 
) 
4496     // Don't check for !HasInternalFlag(wxPG_FL_INITIALIZED) here. Otherwise 
4497     // virtual size calculation may go wrong. 
4498     if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE
) || 
4504     // If virtual height was changed, then recalculate editor control position(s) 
4505     if ( m_pState
->m_vhCalcPending 
) 
4506         CorrectEditorWidgetPosY(); 
4508     m_pState
->EnsureVirtualHeight(); 
4510     wxASSERT_LEVEL_2_MSG( 
4511         m_pState
->GetVirtualHeight() == m_pState
->GetActualVirtualHeight(), 
4512         "VirtualHeight and ActualVirtualHeight should match" 
4515     m_iFlags 
|= wxPG_FL_RECALCULATING_VIRTUAL_SIZE
; 
4517     int x 
= m_pState
->m_width
; 
4518     int y 
= m_pState
->m_virtualHeight
; 
4521     GetClientSize(&width
,&height
); 
4523     // Now adjust virtual size. 
4524     SetVirtualSize(x
, y
); 
4530     // Adjust scrollbars 
4531     if ( HasVirtualWidth() ) 
4533         xAmount 
= x
/wxPG_PIXELS_PER_UNIT
; 
4534         xPos 
= GetScrollPos( wxHORIZONTAL 
); 
4537     if ( forceXPos 
!= -1 ) 
4540     else if ( xPos 
> (xAmount
-(width
/wxPG_PIXELS_PER_UNIT
)) ) 
4543     int yAmount 
= y 
/ wxPG_PIXELS_PER_UNIT
; 
4544     int yPos 
= GetScrollPos( wxVERTICAL 
); 
4546     SetScrollbars( wxPG_PIXELS_PER_UNIT
, wxPG_PIXELS_PER_UNIT
, 
4547                    xAmount
, yAmount
, xPos
, yPos
, true ); 
4549     // This may be needed in addition to calling SetScrollbars() 
4550     // when class inherits from wxScrollHelper instead of 
4551     // actual wxScrolled<T>. 
4554     // Must re-get size now 
4555     GetClientSize(&width
,&height
); 
4557     if ( !HasVirtualWidth() ) 
4559         m_pState
->SetVirtualWidth(width
); 
4566     m_pState
->CheckColumnWidths(); 
4568     if ( GetSelection() ) 
4569         CorrectEditorWidgetSizeX(); 
4571     m_iFlags 
&= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE
; 
4574 // ----------------------------------------------------------------------- 
4576 void wxPropertyGrid::OnResize( wxSizeEvent
& event 
) 
4578     if ( !(m_iFlags 
& wxPG_FL_INITIALIZED
) ) 
4582     GetClientSize(&width
, &height
); 
4587     if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING
) ) 
4589         int dblh 
= (m_lineHeight
*2); 
4590         if ( !m_doubleBuffer 
) 
4592             // Create double buffer bitmap to draw on, if none 
4593             int w 
= (width
>250)?width
:250; 
4594             int h 
= height 
+ dblh
; 
4596             m_doubleBuffer 
= new wxBitmap( w
, h 
); 
4600             int w 
= m_doubleBuffer
->GetWidth(); 
4601             int h 
= m_doubleBuffer
->GetHeight(); 
4603             // Double buffer must be large enough 
4604             if ( w 
< width 
|| h 
< (height
+dblh
) ) 
4606                 if ( w 
< width 
) w 
= width
; 
4607                 if ( h 
< (height
+dblh
) ) h 
= height 
+ dblh
; 
4608                 delete m_doubleBuffer
; 
4609                 m_doubleBuffer 
= new wxBitmap( w
, h 
); 
4614     m_pState
->OnClientWidthChange( width
, event
.GetSize().x 
- m_ncWidth
, true ); 
4615     m_ncWidth 
= event
.GetSize().x
; 
4619         if ( m_pState
->m_itemsAdded 
) 
4620             PrepareAfterItemsAdded(); 
4622             // Without this, virtual size (atleast under wxGTK) will be skewed 
4623             RecalculateVirtualSize(); 
4629 // ----------------------------------------------------------------------- 
4631 void wxPropertyGrid::SetVirtualWidth( int width 
) 
4635         // Disable virtual width 
4636         width 
= GetClientSize().x
; 
4637         ClearInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH
); 
4641         // Enable virtual width 
4642         SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH
); 
4644     m_pState
->SetVirtualWidth( width 
); 
4647 void wxPropertyGrid::SetFocusOnCanvas() 
4649     // To prevent wxPropertyGrid from stealing focus from other controls, 
4650     // only move focus to the grid if it was already in one if its child 
4652     wxWindow
* focus 
= wxWindow::FindFocus(); 
4655         wxWindow
* parent 
= focus
->GetParent(); 
4658             if ( parent 
== this ) 
4663             parent 
= parent
->GetParent(); 
4667     m_editorFocused 
= 0; 
4670 // ----------------------------------------------------------------------- 
4671 // wxPropertyGrid mouse event handling 
4672 // ----------------------------------------------------------------------- 
4674 // selFlags uses same values DoSelectProperty's flags 
4675 // Returns true if event was vetoed. 
4676 bool wxPropertyGrid::SendEvent( int eventType
, wxPGProperty
* p
, 
4678                                 unsigned int selFlags
, 
4679                                 unsigned int column 
) 
4681     // selFlags should have wxPG_SEL_NOVALIDATE if event is not 
4684     // Send property grid event of specific type and with specific property 
4685     wxPropertyGridEvent 
evt( eventType
, m_eventObject
->GetId() ); 
4686     evt
.SetPropertyGrid(this); 
4687     evt
.SetEventObject(m_eventObject
); 
4689     evt
.SetColumn(column
); 
4690     if ( eventType 
== wxEVT_PG_CHANGING 
) 
4693         evt
.SetCanVeto(true); 
4694         m_validationInfo
.m_pValue 
= pValue
; 
4695         evt
.SetupValidationInfo(); 
4700             evt
.SetPropertyValue(p
->GetValue()); 
4702         if ( !(selFlags 
& wxPG_SEL_NOVALIDATE
) ) 
4703             evt
.SetCanVeto(true); 
4706     wxPropertyGridEvent
* prevProcessedEvent 
= m_processedEvent
; 
4707     m_processedEvent 
= &evt
; 
4708     m_eventObject
->HandleWindowEvent(evt
); 
4709     m_processedEvent 
= prevProcessedEvent
; 
4711     return evt
.WasVetoed(); 
4714 // ----------------------------------------------------------------------- 
4716 // Return false if should be skipped 
4717 bool wxPropertyGrid::HandleMouseClick( int x
, unsigned int y
, wxMouseEvent 
&event 
) 
4721     // Need to set focus? 
4722     if ( !(m_iFlags 
& wxPG_FL_FOCUSED
) ) 
4727     wxPropertyGridPageState
* state 
= m_pState
; 
4729     int splitterHitOffset
; 
4730     int columnHit 
= state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4732     wxPGProperty
* p 
= DoGetItemAtY(y
); 
4736         int depth 
= (int)p
->GetDepth() - 1; 
4738         int marginEnds 
= m_marginWidth 
+ ( depth 
* m_subgroup_extramargin 
); 
4740         if ( x 
>= marginEnds 
) 
4744             if ( p
->IsCategory() ) 
4746                 // This is category. 
4747                 wxPropertyCategory
* pwc 
= (wxPropertyCategory
*)p
; 
4749                 int textX 
= m_marginWidth 
+ ((unsigned int)((pwc
->m_depth
-1)*m_subgroup_extramargin
)); 
4751                 // Expand, collapse, activate etc. if click on text or left of splitter. 
4754                      ( x 
< (textX
+pwc
->GetTextExtent(this, m_captionFont
)+(wxPG_CAPRECTXMARGIN
*2)) || 
4759                     if ( !AddToSelectionFromInputEvent( p
, 
4764                     // On double-click, expand/collapse. 
4765                     if ( event
.ButtonDClick() && !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
4767                         if ( pwc
->IsExpanded() ) DoCollapse( p
, true ); 
4768                         else DoExpand( p
, true ); 
4772             else if ( splitterHit 
== -1 ) 
4775                 unsigned int selFlag 
= 0; 
4776                 if ( columnHit 
== 1 ) 
4778                     m_iFlags 
|= wxPG_FL_ACTIVATION_BY_CLICK
; 
4779                     selFlag 
= wxPG_SEL_FOCUS
; 
4781                 if ( !AddToSelectionFromInputEvent( p
, 
4787                 m_iFlags 
&= ~(wxPG_FL_ACTIVATION_BY_CLICK
); 
4789                 if ( p
->GetChildCount() && !p
->IsCategory() ) 
4790                     // On double-click, expand/collapse. 
4791                     if ( event
.ButtonDClick() && !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
4793                         wxPGProperty
* pwc 
= (wxPGProperty
*)p
; 
4794                         if ( pwc
->IsExpanded() ) DoCollapse( p
, true ); 
4795                         else DoExpand( p
, true ); 
4798                 // Do not Skip() the event after selection has been made. 
4799                 // Otherwise default event handling behaviour kicks in 
4800                 // and may revert focus back to the main canvas. 
4805             // click on splitter 
4806                 if ( !(m_windowStyle 
& wxPG_STATIC_SPLITTER
) ) 
4808                     if ( event
.GetEventType() == wxEVT_LEFT_DCLICK 
) 
4810                         // Double-clicking the splitter causes auto-centering 
4811                         if ( m_pState
->GetColumnCount() <= 2 ) 
4813                             ResetColumnSizes( true ); 
4815                             SendEvent(wxEVT_PG_COL_DRAGGING
, 
4818                                       wxPG_SEL_NOVALIDATE
, 
4819                                       (unsigned int)m_draggedSplitter
); 
4822                     else if ( m_dragStatus 
== 0 ) 
4825                         // Begin draggin the splitter 
4829                         DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
); 
4831                         // Allow application to veto dragging 
4832                         if ( !SendEvent(wxEVT_PG_COL_BEGIN_DRAG
, 
4834                                         (unsigned int)splitterHit
) ) 
4838                                 // Changes must be committed here or the 
4839                                 // value won't be drawn correctly 
4840                                 if ( !CommitChangesFromEditor() ) 
4843                                 m_wndEditor
->Show ( false ); 
4846                             if ( !(m_iFlags 
& wxPG_FL_MOUSE_CAPTURED
) ) 
4849                                 m_iFlags 
|= wxPG_FL_MOUSE_CAPTURED
; 
4853                             m_draggedSplitter 
= splitterHit
; 
4854                             m_dragOffset 
= splitterHitOffset
; 
4856                         #if wxPG_REFRESH_CONTROLS 
4857                             // Fixes button disappearance bug 
4859                                 m_wndEditor2
->Show ( false ); 
4862                             m_startingSplitterX 
= x 
- splitterHitOffset
; 
4871             if ( p
->GetChildCount() ) 
4873                 int nx 
= x 
+ m_marginWidth 
- marginEnds
; // Normalize x. 
4875                 // Fine tune cell button x 
4876                 if ( !p
->IsCategory() ) 
4877                     nx 
-= IN_CELL_EXPANDER_BUTTON_X_ADJUST
; 
4879                 if ( (nx 
>= m_gutterWidth 
&& nx 
< (m_gutterWidth
+m_iconWidth
)) ) 
4881                     int y2 
= y 
% m_lineHeight
; 
4882                     if ( (y2 
>= m_buttonSpacingY 
&& y2 
< (m_buttonSpacingY
+m_iconHeight
)) ) 
4884                         // On click on expander button, expand/collapse 
4885                         if ( ((wxPGProperty
*)p
)->IsExpanded() ) 
4886                             DoCollapse( p
, true ); 
4888                             DoExpand( p
, true ); 
4897 // ----------------------------------------------------------------------- 
4899 bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x
), 
4900                                             unsigned int WXUNUSED(y
), 
4901                                             wxMouseEvent
& event 
) 
4905         // Select property here as well 
4906         wxPGProperty
* p 
= m_propHover
; 
4907         AddToSelectionFromInputEvent(p
, m_colHover
, &event
); 
4909         // Send right click event. 
4910         SendEvent( wxEVT_PG_RIGHT_CLICK
, p 
); 
4917 // ----------------------------------------------------------------------- 
4919 bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x
), 
4920                                              unsigned int WXUNUSED(y
), 
4921                                              wxMouseEvent
& event 
) 
4925         // Select property here as well 
4926         wxPGProperty
* p 
= m_propHover
; 
4928         AddToSelectionFromInputEvent(p
, m_colHover
, &event
); 
4930         // Send double-click event. 
4931         SendEvent( wxEVT_PG_DOUBLE_CLICK
, m_propHover 
); 
4938 // ----------------------------------------------------------------------- 
4940 // Return false if should be skipped 
4941 bool wxPropertyGrid::HandleMouseMove( int x
, unsigned int y
, 
4942                                       wxMouseEvent 
&event 
) 
4944     // Safety check (needed because mouse capturing may 
4945     // otherwise freeze the control) 
4946     if ( m_dragStatus 
> 0 && !event
.Dragging() ) 
4948         HandleMouseUp(x
, y
, event
); 
4951     wxPropertyGridPageState
* state 
= m_pState
; 
4953     int splitterHitOffset
; 
4954     int columnHit 
= state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4955     int splitterX 
= x 
- splitterHitOffset
; 
4957     m_colHover 
= columnHit
; 
4959     if ( m_dragStatus 
> 0 ) 
4961         if ( x 
> (m_marginWidth 
+ wxPG_DRAG_MARGIN
) && 
4962              x 
< (m_pState
->m_width 
- wxPG_DRAG_MARGIN
) ) 
4965             int newSplitterX 
= x 
- m_dragOffset
; 
4967             // Splitter redraw required? 
4968             if ( newSplitterX 
!= splitterX 
) 
4971                 DoSetSplitterPosition(newSplitterX
, 
4973                                       wxPG_SPLITTER_REFRESH 
| 
4974                                       wxPG_SPLITTER_FROM_EVENT
); 
4976                 SendEvent(wxEVT_PG_COL_DRAGGING
, 
4979                           wxPG_SEL_NOVALIDATE
, 
4980                           (unsigned int)m_draggedSplitter
); 
4991         int ih 
= m_lineHeight
; 
4994     #if wxPG_SUPPORT_TOOLTIPS 
4995         wxPGProperty
* prevHover 
= m_propHover
; 
4996         unsigned char prevSide 
= m_mouseSide
; 
4998         int curPropHoverY 
= y 
- (y 
% ih
); 
5000         // On which item it hovers 
5003              ( sy 
< m_propHoverY 
|| sy 
>= (m_propHoverY
+ih
) ) 
5006             // Mouse moves on another property 
5008             m_propHover 
= DoGetItemAtY(y
); 
5009             m_propHoverY 
= curPropHoverY
; 
5012             SendEvent( wxEVT_PG_HIGHLIGHTED
, m_propHover 
); 
5015     #if wxPG_SUPPORT_TOOLTIPS 
5016         // Store which side we are on 
5018         if ( columnHit 
== 1 ) 
5020         else if ( columnHit 
== 0 ) 
5024         // If tooltips are enabled, show label or value as a tip 
5025         // in case it doesn't otherwise show in full length. 
5027         if ( m_windowStyle 
& wxPG_TOOLTIPS 
) 
5029             if ( m_propHover 
!= prevHover 
|| prevSide 
!= m_mouseSide 
) 
5031                 if ( m_propHover 
&& !m_propHover
->IsCategory() ) 
5034                     if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS 
) 
5036                         // Show help string as a tooltip 
5037                         wxString tipString 
= m_propHover
->GetHelpString(); 
5039                         SetToolTip(tipString
); 
5043                         // Show cropped value string as a tooltip 
5047                         if ( m_mouseSide 
== 1 ) 
5049                             tipString 
= m_propHover
->m_label
; 
5050                             space 
= splitterX
-m_marginWidth
-3; 
5052                         else if ( m_mouseSide 
== 2 ) 
5054                             tipString 
= m_propHover
->GetDisplayedString(); 
5056                             space 
= m_width 
- splitterX
; 
5057                             if ( m_propHover
->m_flags 
& wxPG_PROP_CUSTOMIMAGE 
) 
5058                                 space 
-= wxPG_CUSTOM_IMAGE_WIDTH 
+ 
5059                                          wxCC_CUSTOM_IMAGE_MARGIN1 
+ 
5060                                          wxCC_CUSTOM_IMAGE_MARGIN2
; 
5066                             GetTextExtent( tipString
, &tw
, &th
, 0, 0 ); 
5068                                 SetToolTip( tipString 
); 
5072                             SetToolTip( m_emptyString 
); 
5079                     SetToolTip( m_emptyString 
); 
5085         if ( splitterHit 
== -1 || 
5087              HasFlag(wxPG_STATIC_SPLITTER
) ) 
5089             // hovering on something else 
5090             if ( m_curcursor 
!= wxCURSOR_ARROW 
) 
5091                 CustomSetCursor( wxCURSOR_ARROW 
); 
5095             // Do not allow splitter cursor on caption items. 
5096             // (also not if we were dragging and its started 
5097             // outside the splitter region) 
5099             if ( !m_propHover
->IsCategory() && 
5103                 // hovering on splitter 
5105                 // NB: Condition disabled since MouseLeave event (from the 
5106                 //     editor control) cannot be reliably detected. 
5107                 //if ( m_curcursor != wxCURSOR_SIZEWE ) 
5108                 CustomSetCursor( wxCURSOR_SIZEWE
, true ); 
5114                 // hovering on something else 
5115                 if ( m_curcursor 
!= wxCURSOR_ARROW 
) 
5116                     CustomSetCursor( wxCURSOR_ARROW 
); 
5121         // Multi select by dragging 
5123         if ( (GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION
) && 
5124              event
.LeftIsDown() && 
5128              !state
->DoIsPropertySelected(m_propHover
) ) 
5130             // Additional requirement is that the hovered property 
5131             // is adjacent to edges of selection. 
5132             const wxArrayPGProperty
& selection 
= GetSelectedProperties(); 
5134             // Since categories cannot be selected along with 'other' 
5135             // properties, exclude them from iterator flags. 
5136             int iterFlags 
= wxPG_ITERATE_VISIBLE 
& (~wxPG_PROP_CATEGORY
); 
5138             for ( int i
=(selection
.size()-1); i
>=0; i
-- ) 
5140                 // TODO: This could be optimized by keeping track of 
5141                 //       which properties are at the edges of selection. 
5142                 wxPGProperty
* selProp 
= selection
[i
]; 
5143                 if ( state
->ArePropertiesAdjacent(m_propHover
, selProp
, 
5146                     DoAddToSelection(m_propHover
); 
5155 // ----------------------------------------------------------------------- 
5157 // Also handles Leaving event 
5158 bool wxPropertyGrid::HandleMouseUp( int x
, unsigned int WXUNUSED(y
), 
5159                                     wxMouseEvent 
&WXUNUSED(event
) ) 
5161     wxPropertyGridPageState
* state 
= m_pState
; 
5165     int splitterHitOffset
; 
5166     state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
5168     // No event type check - basicly calling this method should 
5169     // just stop dragging. 
5170     // Left up after dragged? 
5171     if ( m_dragStatus 
>= 1 ) 
5174     // End Splitter Dragging 
5176         // DO NOT ENABLE FOLLOWING LINE! 
5177         // (it is only here as a reminder to not to do it) 
5180         SendEvent(wxEVT_PG_COL_END_DRAG
, 
5183                   wxPG_SEL_NOVALIDATE
, 
5184                   (unsigned int)m_draggedSplitter
); 
5186         // Disable splitter auto-centering (but only if moved any - 
5187         // otherwise we end up disabling auto-center even after a 
5188         // recentering double-click). 
5189         int posDiff 
= abs(m_startingSplitterX 
- 
5190                           GetSplitterPosition(m_draggedSplitter
)); 
5193             state
->m_dontCenterSplitter 
= true; 
5195         // This is necessary to return cursor 
5196         if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
5199             m_iFlags 
&= ~(wxPG_FL_MOUSE_CAPTURED
); 
5202         // Set back the default cursor, if necessary 
5203         if ( splitterHit 
== -1 || 
5206             CustomSetCursor( wxCURSOR_ARROW 
); 
5211         // Control background needs to be cleared 
5212         wxPGProperty
* selected 
= GetSelection(); 
5213         if ( !(m_iFlags 
& wxPG_FL_PRIMARY_FILLS_ENTIRE
) && selected 
) 
5214             DrawItem( selected 
); 
5218             m_wndEditor
->Show ( true ); 
5221     #if wxPG_REFRESH_CONTROLS 
5222         // Fixes button disappearance bug 
5224             m_wndEditor2
->Show ( true ); 
5227         // This clears the focus. 
5228         m_editorFocused 
= 0; 
5234 // ----------------------------------------------------------------------- 
5236 bool wxPropertyGrid::OnMouseCommon( wxMouseEvent
& event
, int* px
, int* py 
) 
5238     int splitterX 
= GetSplitterPosition(); 
5241     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &ux
, &uy 
); 
5243     wxWindow
* wnd 
= GetEditorControl(); 
5245     // Hide popup on clicks 
5246     if ( event
.GetEventType() != wxEVT_MOTION 
) 
5247         if ( wnd 
&& wnd
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)) ) 
5249             ((wxOwnerDrawnComboBox
*)wnd
)->HidePopup(); 
5255     if ( wnd 
== NULL 
|| m_dragStatus 
|| 
5257            ux 
<= (splitterX 
+ wxPG_SPLITTERX_DETECTMARGIN2
) || 
5258            ux 
>= (r
.x
+r
.width
) || 
5260            event
.m_y 
>= (r
.y
+r
.height
) 
5270         if ( m_curcursor 
!= wxCURSOR_ARROW 
) CustomSetCursor ( wxCURSOR_ARROW 
); 
5275 // ----------------------------------------------------------------------- 
5277 void wxPropertyGrid::OnMouseClick( wxMouseEvent 
&event 
) 
5280     if ( OnMouseCommon( event
, &x
, &y 
) ) 
5282         if ( !HandleMouseClick(x
, y
, event
) ) 
5291 // ----------------------------------------------------------------------- 
5293 void wxPropertyGrid::OnMouseRightClick( wxMouseEvent 
&event 
) 
5296     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &x
, &y 
); 
5297     HandleMouseRightClick(x
,y
,event
); 
5301 // ----------------------------------------------------------------------- 
5303 void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent 
&event 
) 
5305     // Always run standard mouse-down handler as well 
5306     OnMouseClick(event
); 
5309     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &x
, &y 
); 
5310     HandleMouseDoubleClick(x
,y
,event
); 
5312     // Do not Skip() event here - OnMouseClick() call above 
5313     // should have already taken care of it. 
5316 // ----------------------------------------------------------------------- 
5318 void wxPropertyGrid::OnMouseMove( wxMouseEvent 
&event 
) 
5321     if ( OnMouseCommon( event
, &x
, &y 
) ) 
5323         HandleMouseMove(x
,y
,event
); 
5328 // ----------------------------------------------------------------------- 
5330 void wxPropertyGrid::OnMouseUp( wxMouseEvent 
&event 
) 
5333     if ( OnMouseCommon( event
, &x
, &y 
) ) 
5335         if ( !HandleMouseUp(x
, y
, event
) ) 
5344 // ----------------------------------------------------------------------- 
5346 void wxPropertyGrid::OnMouseEntry( wxMouseEvent 
&event 
) 
5348     // This may get called from child control as well, so event's 
5349     // mouse position cannot be relied on. 
5351     if ( event
.Entering() ) 
5353         if ( !(m_iFlags 
& wxPG_FL_MOUSE_INSIDE
) ) 
5355             // TODO: Fix this (detect parent and only do 
5356             //   cursor trick if it is a manager). 
5357             wxASSERT( GetParent() ); 
5358             GetParent()->SetCursor(wxNullCursor
); 
5360             m_iFlags 
|= wxPG_FL_MOUSE_INSIDE
; 
5363             GetParent()->SetCursor(wxNullCursor
); 
5365     else if ( event
.Leaving() ) 
5367         // Without this, wxSpinCtrl editor will sometimes have wrong cursor 
5368         SetCursor( wxNullCursor 
); 
5370         // Get real cursor position 
5371         wxPoint pt 
= ScreenToClient(::wxGetMousePosition()); 
5373         if ( ( pt
.x 
<= 0 || pt
.y 
<= 0 || pt
.x 
>= m_width 
|| pt
.y 
>= m_height 
) ) 
5376                 if ( (m_iFlags 
& wxPG_FL_MOUSE_INSIDE
) ) 
5378                     m_iFlags 
&= ~(wxPG_FL_MOUSE_INSIDE
); 
5382                     wxPropertyGrid::HandleMouseUp ( -1, 10000, event 
); 
5390 // ----------------------------------------------------------------------- 
5392 // Common code used by various OnMouseXXXChild methods. 
5393 bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent 
&event
, int* px
, int *py 
) 
5395     wxWindow
* topCtrlWnd 
= (wxWindow
*)event
.GetEventObject(); 
5396     wxASSERT( topCtrlWnd 
); 
5398     event
.GetPosition(&x
,&y
); 
5400     int splitterX 
= GetSplitterPosition(); 
5402     wxRect r 
= topCtrlWnd
->GetRect(); 
5403     if ( !m_dragStatus 
&& 
5404          x 
> (splitterX
-r
.x
+wxPG_SPLITTERX_DETECTMARGIN2
) && 
5405          y 
>= 0 && y 
< r
.height \
 
5408         if ( m_curcursor 
!= wxCURSOR_ARROW 
) CustomSetCursor ( wxCURSOR_ARROW 
); 
5413         CalcUnscrolledPosition( event
.m_x 
+ r
.x
, event
.m_y 
+ r
.y
, \
 
5420 void wxPropertyGrid::OnMouseClickChild( wxMouseEvent 
&event 
) 
5423     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5425         bool res 
= HandleMouseClick(x
,y
,event
); 
5426         if ( !res 
) event
.Skip(); 
5430 void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent 
&event 
) 
5433     wxASSERT( m_wndEditor 
); 
5434     // These coords may not be exact (about +-2), 
5435     // but that should not matter (right click is about item, not position). 
5436     wxPoint pt 
= m_wndEditor
->GetPosition(); 
5437     CalcUnscrolledPosition( event
.m_x 
+ pt
.x
, event
.m_y 
+ pt
.y
, &x
, &y 
); 
5439     // FIXME: Used to set m_propHover to selection here. Was it really 
5442     bool res 
= HandleMouseRightClick(x
,y
,event
); 
5443     if ( !res 
) event
.Skip(); 
5446 void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent 
&event 
) 
5449     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5451         bool res 
= HandleMouseMove(x
,y
,event
); 
5452         if ( !res 
) event
.Skip(); 
5456 void wxPropertyGrid::OnMouseUpChild( wxMouseEvent 
&event 
) 
5459     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5461         bool res 
= HandleMouseUp(x
,y
,event
); 
5462         if ( !res 
) event
.Skip(); 
5466 // ----------------------------------------------------------------------- 
5467 // wxPropertyGrid keyboard event handling 
5468 // ----------------------------------------------------------------------- 
5470 int wxPropertyGrid::KeyEventToActions(wxKeyEvent 
&event
, int* pSecond
) const 
5472     // Translates wxKeyEvent to wxPG_ACTION_XXX 
5474     int keycode 
= event
.GetKeyCode(); 
5475     int modifiers 
= event
.GetModifiers(); 
5477     wxASSERT( !(modifiers
&~(0xFFFF)) ); 
5479     int hashMapKey 
= (keycode 
& 0xFFFF) | ((modifiers 
& 0xFFFF) << 16); 
5481     wxPGHashMapI2I::const_iterator it 
= m_actionTriggers
.find(hashMapKey
); 
5483     if ( it 
== m_actionTriggers
.end() ) 
5488         int second 
= (it
->second
>>16) & 0xFFFF; 
5492     return (it
->second 
& 0xFFFF); 
5495 void wxPropertyGrid::AddActionTrigger( int action
, int keycode
, int modifiers 
) 
5497     wxASSERT( !(modifiers
&~(0xFFFF)) ); 
5499     int hashMapKey 
= (keycode 
& 0xFFFF) | ((modifiers 
& 0xFFFF) << 16); 
5501     wxPGHashMapI2I::iterator it 
= m_actionTriggers
.find(hashMapKey
); 
5503     if ( it 
!= m_actionTriggers
.end() ) 
5505         // This key combination is already used 
5507         // Can add secondary? 
5508         wxASSERT_MSG( !(it
->second
&~(0xFFFF)), 
5509                       wxT("You can only add up to two separate actions per key combination.") ); 
5511         action 
= it
->second 
| (action
<<16); 
5514     m_actionTriggers
[hashMapKey
] = action
; 
5517 void wxPropertyGrid::ClearActionTriggers( int action 
) 
5519     wxPGHashMapI2I::iterator it
; 
5524         didSomething 
= false; 
5526         for ( it 
= m_actionTriggers
.begin(); 
5527               it 
!= m_actionTriggers
.end(); 
5530             if ( it
->second 
== action 
) 
5532                 m_actionTriggers
.erase(it
); 
5533                 didSomething 
= true; 
5538     while ( didSomething 
); 
5541 void wxPropertyGrid::HandleKeyEvent( wxKeyEvent 
&event
, bool fromChild 
) 
5544     // Handles key event when editor control is not focused. 
5547     wxCHECK2(!m_frozen
, return); 
5549     // Travelsal between items, collapsing/expanding, etc. 
5550     wxPGProperty
* selected 
= GetSelection(); 
5551     int keycode 
= event
.GetKeyCode(); 
5552     bool editorFocused 
= IsEditorFocused(); 
5554     if ( keycode 
== WXK_TAB 
) 
5556         wxWindow
* mainControl
; 
5558         if ( HasInternalFlag(wxPG_FL_IN_MANAGER
) ) 
5559             mainControl 
= GetParent(); 
5563         if ( !event
.ShiftDown() ) 
5565             if ( !editorFocused 
&& m_wndEditor 
) 
5567                 DoSelectProperty( selected
, wxPG_SEL_FOCUS 
); 
5571                 // Tab traversal workaround for platforms on which 
5572                 // wxWindow::Navigate() may navigate into first child 
5573                 // instead of next sibling. Does not work perfectly 
5574                 // in every scenario (for instance, when property grid 
5575                 // is either first or last control). 
5576             #if defined(__WXGTK__) 
5577                 wxWindow
* sibling 
= mainControl
->GetNextSibling(); 
5579                     sibling
->SetFocusFromKbd(); 
5581                 Navigate(wxNavigationKeyEvent::IsForward
); 
5587             if ( editorFocused 
) 
5593             #if defined(__WXGTK__) 
5594                 wxWindow
* sibling 
= mainControl
->GetPrevSibling(); 
5596                     sibling
->SetFocusFromKbd(); 
5598                 Navigate(wxNavigationKeyEvent::IsBackward
); 
5606     // Ignore Alt and Control when they are down alone 
5607     if ( keycode 
== WXK_ALT 
|| 
5608          keycode 
== WXK_CONTROL 
) 
5615     int action 
= KeyEventToActions(event
, &secondAction
); 
5617     if ( editorFocused 
&& action 
== wxPG_ACTION_CANCEL_EDIT 
) 
5620         // Esc cancels any changes 
5621         if ( IsEditorsValueModified() ) 
5623             EditorsValueWasNotModified(); 
5625             // Update the control as well 
5626             selected
->GetEditorClass()-> 
5627                 SetControlStringValue( selected
, 
5629                                        selected
->GetDisplayedString() ); 
5632         OnValidationFailureReset(selected
); 
5638     // Except for TAB, ESC, and any keys specifically dedicated to 
5639     // wxPropertyGrid itself, handle child control events in child control. 
5641          wxPGFindInVector(m_dedicatedKeys
, keycode
) == wxNOT_FOUND 
) 
5643         // Only propagate event if it had modifiers 
5644         if ( !event
.HasModifiers() ) 
5646             event
.StopPropagation(); 
5652     bool wasHandled 
= false; 
5657         if ( ButtonTriggerKeyTest(action
, event
) ) 
5660         wxPGProperty
* p 
= selected
; 
5662         if ( action 
== wxPG_ACTION_EDIT 
&& !editorFocused 
) 
5664             DoSelectProperty( p
, wxPG_SEL_FOCUS 
); 
5668         // Travel and expand/collapse 
5671         if ( p
->GetChildCount() ) 
5673             if ( action 
== wxPG_ACTION_COLLAPSE_PROPERTY 
|| secondAction 
== wxPG_ACTION_COLLAPSE_PROPERTY 
) 
5675                 if ( (m_windowStyle 
& wxPG_HIDE_MARGIN
) || Collapse(p
) ) 
5678             else if ( action 
== wxPG_ACTION_EXPAND_PROPERTY 
|| secondAction 
== wxPG_ACTION_EXPAND_PROPERTY 
) 
5680                 if ( (m_windowStyle 
& wxPG_HIDE_MARGIN
) || Expand(p
) ) 
5687             if ( action 
== wxPG_ACTION_PREV_PROPERTY 
|| secondAction 
== wxPG_ACTION_PREV_PROPERTY 
) 
5691             else if ( action 
== wxPG_ACTION_NEXT_PROPERTY 
|| secondAction 
== wxPG_ACTION_NEXT_PROPERTY 
) 
5697         if ( selectDir 
>= -1 ) 
5699             p 
= wxPropertyGridIterator::OneStep( m_pState
, wxPG_ITERATE_VISIBLE
, p
, selectDir 
); 
5703                 int reopenLabelEditorCol 
= -1; 
5705                 if ( editorFocused 
) 
5707                     // If editor was focused, then make the next editor 
5709                     selFlags 
|= wxPG_SEL_FOCUS
; 
5713                     // Also maintain the same label editor focus state 
5714                     if ( m_labelEditor 
) 
5715                         reopenLabelEditorCol 
= m_selColumn
; 
5718                 DoSelectProperty(p
, selFlags
); 
5720                 if ( reopenLabelEditorCol 
>= 0 ) 
5721                     DoBeginLabelEdit(reopenLabelEditorCol
); 
5728         // If nothing was selected, select the first item now 
5729         // (or navigate out of tab). 
5730         if ( action 
!= wxPG_ACTION_CANCEL_EDIT 
&& secondAction 
!= wxPG_ACTION_CANCEL_EDIT 
) 
5732             wxPGProperty
* p 
= wxPropertyGridInterface::GetFirst(); 
5733             if ( p 
) DoSelectProperty(p
); 
5742 // ----------------------------------------------------------------------- 
5744 void wxPropertyGrid::OnKey( wxKeyEvent 
&event 
) 
5746     // If there was editor open and focused, then this event should not 
5747     // really be processed here. 
5748     if ( IsEditorFocused() ) 
5750         // However, if event had modifiers, it is probably still best 
5752         if ( event
.HasModifiers() ) 
5755             event
.StopPropagation(); 
5759     HandleKeyEvent(event
, false); 
5762 // ----------------------------------------------------------------------- 
5764 bool wxPropertyGrid::ButtonTriggerKeyTest( int action
, wxKeyEvent
& event 
) 
5769         action 
= KeyEventToActions(event
, &secondAction
); 
5772     // Does the keycode trigger button? 
5773     if ( action 
== wxPG_ACTION_PRESS_BUTTON 
&& 
5776         wxCommandEvent 
evt(wxEVT_COMMAND_BUTTON_CLICKED
, m_wndEditor2
->GetId()); 
5777         GetEventHandler()->AddPendingEvent(evt
); 
5784 // ----------------------------------------------------------------------- 
5786 void wxPropertyGrid::OnChildKeyDown( wxKeyEvent 
&event 
) 
5788     HandleKeyEvent(event
, true); 
5791 // ----------------------------------------------------------------------- 
5792 // wxPropertyGrid miscellaneous event handling 
5793 // ----------------------------------------------------------------------- 
5795 void wxPropertyGrid::OnIdle( wxIdleEvent
& WXUNUSED(event
) ) 
5798     // Check if the focus is in this control or one of its children 
5799     wxWindow
* newFocused 
= wxWindow::FindFocus(); 
5801     if ( newFocused 
!= m_curFocused 
) 
5802         HandleFocusChange( newFocused 
); 
5805     // Check if top-level parent has changed 
5806     if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING 
) 
5808         wxWindow
* tlp 
= ::wxGetTopLevelParent(this); 
5814     // Resolve pending property removals 
5815     if ( m_deletedProperties
.size() > 0 ) 
5817         wxArrayPGProperty
& arr 
= m_deletedProperties
; 
5818         for ( unsigned int i
=0; i
<arr
.size(); i
++ ) 
5820             DeleteProperty(arr
[i
]); 
5824     if ( m_removedProperties
.size() > 0 ) 
5826         wxArrayPGProperty
& arr 
= m_removedProperties
; 
5827         for ( unsigned int i
=0; i
<arr
.size(); i
++ ) 
5829             RemoveProperty(arr
[i
]); 
5835 bool wxPropertyGrid::IsEditorFocused() const 
5837     wxWindow
* focus 
= wxWindow::FindFocus(); 
5839     if ( focus 
== m_wndEditor 
|| focus 
== m_wndEditor2 
|| 
5840          focus 
== GetEditorControl() ) 
5846 // Called by focus event handlers. newFocused is the window that becomes focused. 
5847 void wxPropertyGrid::HandleFocusChange( wxWindow
* newFocused 
) 
5850     // Never allow focus to be changed when handling editor event. 
5851     // Especially because they may be displaing a dialog which 
5852     // could cause all kinds of weird (native) focus changes. 
5853     if ( HasInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
) ) 
5856     unsigned int oldFlags 
= m_iFlags
; 
5857     bool wasEditorFocused 
= false; 
5858     wxWindow
* wndEditor 
= m_wndEditor
; 
5860     m_iFlags 
&= ~(wxPG_FL_FOCUSED
); 
5862     wxWindow
* parent 
= newFocused
; 
5864     // This must be one of nextFocus' parents. 
5867         if ( parent 
== wndEditor 
) 
5869             wasEditorFocused 
= true; 
5871         // Use m_eventObject, which is either wxPropertyGrid or 
5872         // wxPropertyGridManager, as appropriate. 
5873         else if ( parent 
== m_eventObject 
) 
5875             m_iFlags 
|= wxPG_FL_FOCUSED
; 
5878         parent 
= parent
->GetParent(); 
5881     // Notify editor control when it receives a focus 
5882     if ( wasEditorFocused 
&& m_curFocused 
!= newFocused 
) 
5884         wxPGProperty
* p 
= GetSelection(); 
5887             const wxPGEditor
* editor 
= p
->GetEditorClass(); 
5888             ResetEditorAppearance(); 
5889             editor
->OnFocus(p
, GetEditorControl()); 
5893     m_curFocused 
= newFocused
; 
5895     if ( (m_iFlags 
& wxPG_FL_FOCUSED
) != 
5896          (oldFlags 
& wxPG_FL_FOCUSED
) ) 
5898         if ( !(m_iFlags 
& wxPG_FL_FOCUSED
) ) 
5900             // Need to store changed value 
5901             CommitChangesFromEditor(); 
5907             // Preliminary code for tab-order respecting 
5908             // tab-traversal (but should be moved to 
5911             wxWindow* prevFocus = event.GetWindow(); 
5912             wxWindow* useThis = this; 
5913             if ( m_iFlags & wxPG_FL_IN_MANAGER ) 
5914                 useThis = GetParent(); 
5917                  prevFocus->GetParent() == useThis->GetParent() ) 
5919                 wxList& children = useThis->GetParent()->GetChildren(); 
5921                 wxNode* node = children.Find(prevFocus); 
5923                 if ( node->GetNext() && 
5924                      useThis == node->GetNext()->GetData() ) 
5925                     DoSelectProperty(GetFirst()); 
5926                 else if ( node->GetPrevious () && 
5927                           useThis == node->GetPrevious()->GetData() ) 
5928                     DoSelectProperty(GetLastProperty()); 
5935         wxPGProperty
* selected 
= GetSelection(); 
5936         if ( selected 
&& (m_iFlags 
& wxPG_FL_INITIALIZED
) ) 
5937             DrawItem( selected 
); 
5941 void wxPropertyGrid::OnFocusEvent( wxFocusEvent
& event 
) 
5943     if ( event
.GetEventType() == wxEVT_SET_FOCUS 
) 
5944         HandleFocusChange((wxWindow
*)event
.GetEventObject()); 
5945     // Line changed to "else" when applying wxPropertyGrid patch #1675902 
5946     //else if ( event.GetWindow() ) 
5948         HandleFocusChange(event
.GetWindow()); 
5953 // ----------------------------------------------------------------------- 
5955 void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent
& event 
) 
5957     HandleFocusChange((wxWindow
*)event
.GetEventObject()); 
5961 // ----------------------------------------------------------------------- 
5963 void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent 
&event 
) 
5965     m_iFlags 
|= wxPG_FL_SCROLLED
; 
5970 // ----------------------------------------------------------------------- 
5972 void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent
& WXUNUSED(event
) ) 
5974     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
5976         m_iFlags 
&= ~(wxPG_FL_MOUSE_CAPTURED
); 
5980 // ----------------------------------------------------------------------- 
5981 // Property editor related functions 
5982 // ----------------------------------------------------------------------- 
5984 // noDefCheck = true prevents infinite recursion. 
5985 wxPGEditor
* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor
* editorClass
, 
5986                                                    const wxString
& editorName
, 
5989     wxASSERT( editorClass 
); 
5991     if ( !noDefCheck 
&& wxPGGlobalVars
->m_mapEditorClasses
.empty() ) 
5992         RegisterDefaultEditors(); 
5994     wxString name 
= editorName
; 
5996         name 
= editorClass
->GetName(); 
5998     // Existing editor under this name? 
5999     wxPGHashMapS2P::iterator vt_it 
= wxPGGlobalVars
->m_mapEditorClasses
.find(name
); 
6001     if ( vt_it 
!= wxPGGlobalVars
->m_mapEditorClasses
.end() ) 
6003         // If this name was already used, try class name. 
6004         name 
= editorClass
->GetClassInfo()->GetClassName(); 
6005         vt_it 
= wxPGGlobalVars
->m_mapEditorClasses
.find(name
); 
6008     wxCHECK_MSG( vt_it 
== wxPGGlobalVars
->m_mapEditorClasses
.end(), 
6009                  (wxPGEditor
*) vt_it
->second
, 
6010                  "Editor with given name was already registered" ); 
6012     wxPGGlobalVars
->m_mapEditorClasses
[name
] = (void*)editorClass
; 
6017 // Use this in RegisterDefaultEditors. 
6018 #define wxPGRegisterDefaultEditorClass(EDITOR) \ 
6019     if ( wxPGEditor_##EDITOR == NULL ) \ 
6021         wxPGEditor_##EDITOR = wxPropertyGrid::RegisterEditorClass( \ 
6022             new wxPG##EDITOR##Editor, true ); \ 
6025 // Registers all default editor classes 
6026 void wxPropertyGrid::RegisterDefaultEditors() 
6028     wxPGRegisterDefaultEditorClass( TextCtrl 
); 
6029     wxPGRegisterDefaultEditorClass( Choice 
); 
6030     wxPGRegisterDefaultEditorClass( ComboBox 
); 
6031     wxPGRegisterDefaultEditorClass( TextCtrlAndButton 
); 
6032 #if wxPG_INCLUDE_CHECKBOX 
6033     wxPGRegisterDefaultEditorClass( CheckBox 
); 
6035     wxPGRegisterDefaultEditorClass( ChoiceAndButton 
); 
6037     // Register SpinCtrl etc. editors before use 
6038     RegisterAdditionalEditors(); 
6041 // ----------------------------------------------------------------------- 
6042 // wxPGStringTokenizer 
6043 //   Needed to handle C-style string lists (e.g. "str1" "str2") 
6044 // ----------------------------------------------------------------------- 
6046 wxPGStringTokenizer::wxPGStringTokenizer( const wxString
& str
, wxChar delimeter 
) 
6047     : m_str(&str
), m_curPos(str
.begin()), m_delimeter(delimeter
) 
6051 wxPGStringTokenizer::~wxPGStringTokenizer() 
6055 bool wxPGStringTokenizer::HasMoreTokens() 
6057     const wxString
& str 
= *m_str
; 
6059     wxString::const_iterator i 
= m_curPos
; 
6061     wxUniChar delim 
= m_delimeter
; 
6063     wxUniChar prev_a 
= wxT('\0'); 
6065     bool inToken 
= false; 
6067     while ( i 
!= str
.end() ) 
6076                 m_readyToken
.clear(); 
6081             if ( prev_a 
!= wxT('\\') ) 
6085                     if ( a 
!= wxT('\\') ) 
6105     m_curPos 
= str
.end(); 
6113 wxString 
wxPGStringTokenizer::GetNextToken() 
6115     return m_readyToken
; 
6118 // ----------------------------------------------------------------------- 
6120 // ----------------------------------------------------------------------- 
6122 wxPGChoiceEntry::wxPGChoiceEntry() 
6123     : wxPGCell(), m_value(wxPG_INVALID_VALUE
) 
6127 // ----------------------------------------------------------------------- 
6129 // ----------------------------------------------------------------------- 
6131 wxPGChoicesData::wxPGChoicesData() 
6135 wxPGChoicesData::~wxPGChoicesData() 
6140 void wxPGChoicesData::Clear() 
6145 void wxPGChoicesData::CopyDataFrom( wxPGChoicesData
* data 
) 
6147     wxASSERT( m_items
.size() == 0 ); 
6149     m_items 
= data
->m_items
; 
6152 wxPGChoiceEntry
& wxPGChoicesData::Insert( int index
, 
6153                                           const wxPGChoiceEntry
& item 
) 
6155     wxVector
<wxPGChoiceEntry
>::iterator it
; 
6159         index 
= (int) m_items
.size(); 
6163         it 
= m_items
.begin() + index
; 
6166     m_items
.insert(it
, item
); 
6168     wxPGChoiceEntry
& ownEntry 
= m_items
[index
]; 
6170     // Need to fix value? 
6171     if ( ownEntry
.GetValue() == wxPG_INVALID_VALUE 
) 
6172         ownEntry
.SetValue(index
); 
6177 // ----------------------------------------------------------------------- 
6178 // wxPropertyGridEvent 
6179 // ----------------------------------------------------------------------- 
6181 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent
, wxCommandEvent
) 
6184 wxDEFINE_EVENT( wxEVT_PG_SELECTED
, wxPropertyGridEvent 
); 
6185 wxDEFINE_EVENT( wxEVT_PG_CHANGING
, wxPropertyGridEvent 
); 
6186 wxDEFINE_EVENT( wxEVT_PG_CHANGED
, wxPropertyGridEvent 
); 
6187 wxDEFINE_EVENT( wxEVT_PG_HIGHLIGHTED
, wxPropertyGridEvent 
); 
6188 wxDEFINE_EVENT( wxEVT_PG_RIGHT_CLICK
, wxPropertyGridEvent 
); 
6189 wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED
, wxPropertyGridEvent 
); 
6190 wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED
, wxPropertyGridEvent 
); 
6191 wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED
, wxPropertyGridEvent 
); 
6192 wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK
, wxPropertyGridEvent 
); 
6193 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN
, wxPropertyGridEvent 
); 
6194 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING
, wxPropertyGridEvent 
); 
6195 wxDEFINE_EVENT( wxEVT_PG_COL_BEGIN_DRAG
, wxPropertyGridEvent 
); 
6196 wxDEFINE_EVENT( wxEVT_PG_COL_DRAGGING
, wxPropertyGridEvent 
); 
6197 wxDEFINE_EVENT( wxEVT_PG_COL_END_DRAG
, wxPropertyGridEvent 
); 
6199 // ----------------------------------------------------------------------- 
6201 void wxPropertyGridEvent::Init() 
6203     m_validationInfo 
= NULL
; 
6206     m_wasVetoed 
= false; 
6210 // ----------------------------------------------------------------------- 
6212 wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType
, int id
) 
6213     : wxCommandEvent(commandType
,id
) 
6219 // ----------------------------------------------------------------------- 
6221 wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent
& event
) 
6222     : wxCommandEvent(event
) 
6224     m_eventType 
= event
.GetEventType(); 
6225     m_eventObject 
= event
.m_eventObject
; 
6227     OnPropertyGridSet(); 
6228     m_property 
= event
.m_property
; 
6229     m_validationInfo 
= event
.m_validationInfo
; 
6230     m_canVeto 
= event
.m_canVeto
; 
6231     m_wasVetoed 
= event
.m_wasVetoed
; 
6234 // ----------------------------------------------------------------------- 
6236 void wxPropertyGridEvent::OnPropertyGridSet() 
6242     wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
6244     m_pg
->m_liveEvents
.push_back(this); 
6247 // ----------------------------------------------------------------------- 
6249 wxPropertyGridEvent::~wxPropertyGridEvent() 
6254         wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
6257         // Use iterate from the back since it is more likely that the event 
6258         // being desroyed is at the end of the array. 
6259         wxVector
<wxPropertyGridEvent
*>& liveEvents 
= m_pg
->m_liveEvents
; 
6261         for ( int i 
= liveEvents
.size()-1; i 
>= 0; i
-- ) 
6263             if ( liveEvents
[i
] == this ) 
6265                 liveEvents
.erase(liveEvents
.begin() + i
); 
6272 // ----------------------------------------------------------------------- 
6274 wxEvent
* wxPropertyGridEvent::Clone() const 
6276     return new wxPropertyGridEvent( *this ); 
6279 // ----------------------------------------------------------------------- 
6280 // wxPropertyGridPopulator 
6281 // ----------------------------------------------------------------------- 
6283 wxPropertyGridPopulator::wxPropertyGridPopulator() 
6287     wxPGGlobalVars
->m_offline
++; 
6290 // ----------------------------------------------------------------------- 
6292 void wxPropertyGridPopulator::SetState( wxPropertyGridPageState
* state 
) 
6295     m_propHierarchy
.clear(); 
6298 // ----------------------------------------------------------------------- 
6300 void wxPropertyGridPopulator::SetGrid( wxPropertyGrid
* pg 
) 
6306 // ----------------------------------------------------------------------- 
6308 wxPropertyGridPopulator::~wxPropertyGridPopulator() 
6311     // Free unused sets of choices 
6312     wxPGHashMapS2P::iterator it
; 
6314     for( it 
= m_dictIdChoices
.begin(); it 
!= m_dictIdChoices
.end(); ++it 
) 
6316         wxPGChoicesData
* data 
= (wxPGChoicesData
*) it
->second
; 
6323         m_pg
->GetPanel()->Refresh(); 
6325     wxPGGlobalVars
->m_offline
--; 
6328 // ----------------------------------------------------------------------- 
6330 wxPGProperty
* wxPropertyGridPopulator::Add( const wxString
& propClass
, 
6331                                             const wxString
& propLabel
, 
6332                                             const wxString
& propName
, 
6333                                             const wxString
* propValue
, 
6334                                             wxPGChoices
* pChoices 
) 
6336     wxClassInfo
* classInfo 
= wxClassInfo::FindClass(propClass
); 
6337     wxPGProperty
* parent 
= GetCurParent(); 
6339     if ( parent
->HasFlag(wxPG_PROP_AGGREGATE
) ) 
6341         ProcessError(wxString::Format(wxT("new children cannot be added to '%s'"),parent
->GetName().c_str())); 
6345     if ( !classInfo 
|| !classInfo
->IsKindOf(CLASSINFO(wxPGProperty
)) ) 
6347         ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass
.c_str())); 
6351     wxPGProperty
* property 
= (wxPGProperty
*) classInfo
->CreateObject(); 
6353     property
->SetLabel(propLabel
); 
6354     property
->DoSetName(propName
); 
6356     if ( pChoices 
&& pChoices
->IsOk() ) 
6357         property
->SetChoices(*pChoices
); 
6359     m_state
->DoInsert(parent
, -1, property
); 
6362         property
->SetValueFromString( *propValue
, wxPG_FULL_VALUE
| 
6363                                                   wxPG_PROGRAMMATIC_VALUE 
); 
6368 // ----------------------------------------------------------------------- 
6370 void wxPropertyGridPopulator::AddChildren( wxPGProperty
* property 
) 
6372     m_propHierarchy
.push_back(property
); 
6373     DoScanForChildren(); 
6374     m_propHierarchy
.pop_back(); 
6377 // ----------------------------------------------------------------------- 
6379 wxPGChoices 
wxPropertyGridPopulator::ParseChoices( const wxString
& choicesString
, 
6380                                                    const wxString
& idString 
) 
6382     wxPGChoices choices
; 
6385     if ( choicesString
[0] == wxT('@') ) 
6387         wxString ids 
= choicesString
.substr(1); 
6388         wxPGHashMapS2P::iterator it 
= m_dictIdChoices
.find(ids
); 
6389         if ( it 
== m_dictIdChoices
.end() ) 
6390             ProcessError(wxString::Format(wxT("No choices defined for id '%s'"),ids
.c_str())); 
6392             choices
.AssignData((wxPGChoicesData
*)it
->second
); 
6397         if ( !idString
.empty() ) 
6399             wxPGHashMapS2P::iterator it 
= m_dictIdChoices
.find(idString
); 
6400             if ( it 
!= m_dictIdChoices
.end() ) 
6402                 choices
.AssignData((wxPGChoicesData
*)it
->second
); 
6409             // Parse choices string 
6410             wxString::const_iterator it 
= choicesString
.begin(); 
6414             bool labelValid 
= false; 
6416             for ( ; it 
!= choicesString
.end(); ++it 
) 
6422                     if ( c 
== wxT('"') ) 
6427                             if ( !value
.ToLong(&l
, 0) ) l 
= wxPG_INVALID_VALUE
; 
6428                             choices
.Add(label
, l
); 
6431                         //wxLogDebug(wxT("%s, %s"),label.c_str(),value.c_str()); 
6436                     else if ( c 
== wxT('=') ) 
6443                     else if ( state 
== 2 && (wxIsalnum(c
) || c 
== wxT('x')) ) 
6450                     if ( c 
== wxT('"') ) 
6463                 if ( !value
.ToLong(&l
, 0) ) l 
= wxPG_INVALID_VALUE
; 
6464                 choices
.Add(label
, l
); 
6467             if ( !choices
.IsOk() ) 
6469                 choices
.EnsureData(); 
6473             if ( !idString
.empty() ) 
6474                 m_dictIdChoices
[idString
] = choices
.GetData(); 
6481 // ----------------------------------------------------------------------- 
6483 bool wxPropertyGridPopulator::ToLongPCT( const wxString
& s
, long* pval
, long max 
) 
6485     if ( s
.Last() == wxT('%') ) 
6487         wxString s2 
= s
.substr(0,s
.length()-1); 
6489         if ( s2
.ToLong(&val
, 10) ) 
6491             *pval 
= (val
*max
)/100; 
6497     return s
.ToLong(pval
, 10); 
6500 // ----------------------------------------------------------------------- 
6502 bool wxPropertyGridPopulator::AddAttribute( const wxString
& name
, 
6503                                             const wxString
& type
, 
6504                                             const wxString
& value 
) 
6506     int l 
= m_propHierarchy
.size(); 
6510     wxPGProperty
* p 
= m_propHierarchy
[l
-1]; 
6511     wxString valuel 
= value
.Lower(); 
6519         if ( valuel 
== wxT("true") || valuel 
== wxT("yes") || valuel 
== wxT("1") ) 
6521         else if ( valuel 
== wxT("false") || valuel 
== wxT("no") || valuel 
== wxT("0") ) 
6523         else if ( value
.ToLong(&v
, 0) ) 
6530         if ( type 
== wxT("string") ) 
6534         else if ( type 
== wxT("int") ) 
6537             value
.ToLong(&v
, 0); 
6540         else if ( type 
== wxT("bool") ) 
6542             if ( valuel 
== wxT("true") || valuel 
== wxT("yes") || valuel 
== wxT("1") ) 
6549             ProcessError(wxString::Format(wxT("Invalid attribute type '%s'"),type
.c_str())); 
6554     p
->SetAttribute( name
, variant 
); 
6559 // ----------------------------------------------------------------------- 
6561 void wxPropertyGridPopulator::ProcessError( const wxString
& msg 
) 
6563     wxLogError(_("Error in resource: %s"),msg
.c_str()); 
6566 // ----------------------------------------------------------------------- 
6568 #endif  // wxUSE_PROPGRID