1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/propgrid/propgrid.cpp 
   3 // Purpose:     wxPropertyGrid 
   4 // Author:      Jaakko Salli 
   8 // Copyright:   (c) Jaakko Salli 
   9 // Licence:     wxWindows license 
  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" 
  67 // Two pics for the expand / collapse buttons. 
  68 // Files are not supplied with this project (since it is 
  69 // recommended to use either custom or native rendering). 
  70 // If you want them, get wxTreeMultiCtrl by Jorgen Bodde, 
  71 // and copy xpm files from archive to wxPropertyGrid src directory 
  72 // (and also comment/undef wxPG_ICON_WIDTH in propGrid.h 
  73 // and set wxPG_USE_RENDERER_NATIVE to 0). 
  74 #ifndef wxPG_ICON_WIDTH 
  75   #if defined(__WXMAC__) 
  76     #include "mac_collapse.xpm" 
  77     #include "mac_expand.xpm" 
  78   #elif defined(__WXGTK__) 
  79     #include "linux_collapse.xpm" 
  80     #include "linux_expand.xpm" 
  82     #include "default_collapse.xpm" 
  83     #include "default_expand.xpm" 
  88 //#define wxPG_TEXT_INDENT                4 // For the wxComboControl 
  89 //#define wxPG_ALLOW_CLIPPING             1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored 
  90 #define wxPG_GUTTER_DIV                 3 // gutter is max(iconwidth/gutter_div,gutter_min) 
  91 #define wxPG_GUTTER_MIN                 3 // gutter before and after image of [+] or [-] 
  92 #define wxPG_YSPACING_MIN               1 
  93 #define wxPG_DEFAULT_VSPACING           2 // This matches .NET propertygrid's value, 
  94                                           // but causes normal combobox to spill out under MSW 
  96 //#define wxPG_OPTIMAL_WIDTH              200 // Arbitrary 
  98 //#define wxPG_MIN_SCROLLBAR_WIDTH        10 // Smallest scrollbar width on any platform 
  99                                            // Must be larger than largest control border 
 103 #define wxPG_DEFAULT_CURSOR             wxNullCursor 
 106 //#define wxPG_NAT_CHOICE_BORDER_ANY   0 
 108 //#define wxPG_HIDER_BUTTON_HEIGHT        25 
 110 #define wxPG_PIXELS_PER_UNIT            m_lineHeight 
 112 #ifdef wxPG_ICON_WIDTH 
 113   #define m_iconHeight m_iconWidth 
 116 //#define wxPG_TOOLTIP_DELAY              1000 
 118 // ----------------------------------------------------------------------- 
 121 void wxPropertyGrid::AutoGetTranslation ( bool enable 
) 
 123     wxPGGlobalVars
->m_autoGetTranslation 
= enable
; 
 126 void wxPropertyGrid::AutoGetTranslation ( bool ) { } 
 129 // ----------------------------------------------------------------------- 
 131 const char wxPropertyGridNameStr
[] = "wxPropertyGrid"; 
 133 // ----------------------------------------------------------------------- 
 134 // Statics in one class for easy destruction. 
 135 // ----------------------------------------------------------------------- 
 137 #include "wx/module.h" 
 139 class wxPGGlobalVarsClassManager 
: public wxModule
 
 141     DECLARE_DYNAMIC_CLASS(wxPGGlobalVarsClassManager
) 
 143     wxPGGlobalVarsClassManager() {} 
 144     virtual bool OnInit() { wxPGGlobalVars 
= new wxPGGlobalVarsClass(); return true; } 
 145     virtual void OnExit() { delete wxPGGlobalVars
; wxPGGlobalVars 
= NULL
; } 
 148 IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager
, wxModule
) 
 151 // When wxPG is loaded dynamically after the application is already running 
 152 // then the built-in module system won't pick this one up.  Add it manually. 
 153 void wxPGInitResourceModule() 
 155     wxModule
* module = new wxPGGlobalVarsClassManager
; 
 157     wxModule::RegisterModule(module); 
 160 wxPGGlobalVarsClass
* wxPGGlobalVars 
= NULL
; 
 163 wxPGGlobalVarsClass::wxPGGlobalVarsClass() 
 165     wxPGProperty::sm_wxPG_LABEL 
= new wxString(wxPG_LABEL_STRING
); 
 167     m_boolChoices
.Add(_("False")); 
 168     m_boolChoices
.Add(_("True")); 
 170     m_fontFamilyChoices 
= NULL
; 
 172     m_defaultRenderer 
= new wxPGDefaultRenderer(); 
 174     m_autoGetTranslation 
= false; 
 182     // Prepare some shared variants 
 183     m_vEmptyString 
= wxString(); 
 185     m_vMinusOne 
= (long) -1; 
 189     // Prepare cached string constants 
 190     m_strstring 
= wxS("string"); 
 191     m_strlong 
= wxS("long"); 
 192     m_strbool 
= wxS("bool"); 
 193     m_strlist 
= wxS("list"); 
 194     m_strDefaultValue 
= wxS("DefaultValue"); 
 195     m_strMin 
= wxS("Min"); 
 196     m_strMax 
= wxS("Max"); 
 197     m_strUnits 
= wxS("Units"); 
 198     m_strInlineHelp 
= wxS("InlineHelp"); 
 204 wxPGGlobalVarsClass::~wxPGGlobalVarsClass() 
 208     delete m_defaultRenderer
; 
 210     // This will always have one ref 
 211     delete m_fontFamilyChoices
; 
 214     for ( i
=0; i
<m_arrValidators
.size(); i
++ ) 
 215         delete ((wxValidator
*)m_arrValidators
[i
]); 
 219     // Destroy value type class instances. 
 220     wxPGHashMapS2P::iterator vt_it
; 
 222     // Destroy editor class instances. 
 223     // iterate over all the elements in the class 
 224     for( vt_it 
= m_mapEditorClasses
.begin(); vt_it 
!= m_mapEditorClasses
.end(); ++vt_it 
) 
 226         delete ((wxPGEditor
*)vt_it
->second
); 
 229     delete wxPGProperty::sm_wxPG_LABEL
; 
 232 void wxPropertyGridInitGlobalsIfNeeded() 
 236 // ----------------------------------------------------------------------- 
 238 // ----------------------------------------------------------------------- 
 240 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid
, wxScrolledWindow
) 
 242 BEGIN_EVENT_TABLE(wxPropertyGrid
, wxScrolledWindow
) 
 243   EVT_IDLE(wxPropertyGrid::OnIdle
) 
 244   EVT_PAINT(wxPropertyGrid::OnPaint
) 
 245   EVT_SIZE(wxPropertyGrid::OnResize
) 
 246   EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry
) 
 247   EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry
) 
 248   EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange
) 
 249   EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent
) 
 250   EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent
) 
 251   EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent
) 
 252   EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent
) 
 253   EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged
) 
 254   EVT_MOTION(wxPropertyGrid::OnMouseMove
) 
 255   EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick
) 
 256   EVT_LEFT_UP(wxPropertyGrid::OnMouseUp
) 
 257   EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick
) 
 258   EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick
) 
 259   EVT_KEY_DOWN(wxPropertyGrid::OnKey
) 
 262 // ----------------------------------------------------------------------- 
 264 wxPropertyGrid::wxPropertyGrid() 
 270 // ----------------------------------------------------------------------- 
 272 wxPropertyGrid::wxPropertyGrid( wxWindow 
*parent
, 
 277                                 const wxString
& name 
) 
 281     Create(parent
,id
,pos
,size
,style
,name
); 
 284 // ----------------------------------------------------------------------- 
 286 bool wxPropertyGrid::Create( wxWindow 
*parent
, 
 291                              const wxString
& name 
) 
 294     if (!(style
&wxBORDER_MASK
)) 
 296         style 
|= wxBORDER_THEME
; 
 301     // Filter out wxTAB_TRAVERSAL - we will handle TABs manually 
 302     style 
&= ~(wxTAB_TRAVERSAL
); 
 303     style 
|= wxWANTS_CHARS
; 
 305     wxScrolledWindow::Create(parent
,id
,pos
,size
,style
,name
); 
 312 // ----------------------------------------------------------------------- 
 315 // Initialize values to defaults 
 317 void wxPropertyGrid::Init1() 
 319     // Register editor classes, if necessary. 
 320     if ( wxPGGlobalVars
->m_mapEditorClasses
.empty() ) 
 321         wxPropertyGrid::RegisterDefaultEditors(); 
 325     m_wndEditor 
= m_wndEditor2 
= NULL
; 
 329     m_labelEditor 
= NULL
; 
 330     m_labelEditorProperty 
= NULL
; 
 331     m_eventObject 
= this; 
 333     m_processedEvent 
= NULL
; 
 334     m_sortFunction 
= NULL
; 
 335     m_inDoPropertyChanged 
= 0; 
 336     m_inCommitChangesFromEditor 
= 0; 
 337     m_inDoSelectProperty 
= 0; 
 338     m_permanentValidationFailureBehavior 
= wxPG_VFB_DEFAULT
; 
 344     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY
, WXK_RIGHT 
); 
 345     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY
, WXK_DOWN 
); 
 346     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY
, WXK_LEFT 
); 
 347     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY
, WXK_UP 
); 
 348     AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY
, WXK_RIGHT
); 
 349     AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY
, WXK_LEFT
); 
 350     AddActionTrigger( wxPG_ACTION_CANCEL_EDIT
, WXK_ESCAPE 
); 
 351     AddActionTrigger( wxPG_ACTION_PRESS_BUTTON
, WXK_DOWN
, wxMOD_ALT 
); 
 352     AddActionTrigger( wxPG_ACTION_PRESS_BUTTON
, WXK_F4 
); 
 354     m_coloursCustomized 
= 0; 
 357 #if wxPG_DOUBLE_BUFFER 
 358     m_doubleBuffer 
= NULL
; 
 361 #ifndef wxPG_ICON_WIDTH 
 367     m_iconWidth 
= wxPG_ICON_WIDTH
; 
 372     m_gutterWidth 
= wxPG_GUTTER_MIN
; 
 373     m_subgroup_extramargin 
= 10; 
 377     m_width 
= m_height 
= 0; 
 379     m_commonValues
.push_back(new wxPGCommonValue(_("Unspecified"), wxPGGlobalVars
->m_defaultRenderer
) ); 
 382     m_chgInfo_changedProperty 
= NULL
; 
 385 // ----------------------------------------------------------------------- 
 388 // Initialize after parent etc. set 
 390 void wxPropertyGrid::Init2() 
 392     wxASSERT( !(m_iFlags 
& wxPG_FL_INITIALIZED 
) ); 
 395    // Smaller controls on Mac 
 396    SetWindowVariant(wxWINDOW_VARIANT_SMALL
); 
 399     // Now create state, if one didn't exist already 
 400     // (wxPropertyGridManager might have created it for us). 
 403         m_pState 
= CreateState(); 
 404         m_pState
->m_pPropGrid 
= this; 
 405         m_iFlags 
|= wxPG_FL_CREATEDSTATE
; 
 408     if ( !(m_windowStyle 
& wxPG_SPLITTER_AUTO_CENTER
) ) 
 409         m_pState
->m_dontCenterSplitter 
= true; 
 411     if ( m_windowStyle 
& wxPG_HIDE_CATEGORIES 
) 
 413         m_pState
->InitNonCatMode(); 
 415         m_pState
->m_properties 
= m_pState
->m_abcArray
; 
 418     GetClientSize(&m_width
,&m_height
); 
 420 #ifndef wxPG_ICON_WIDTH 
 421     // create two bitmap nodes for drawing 
 422     m_expandbmp 
= new wxBitmap(expand_xpm
); 
 423     m_collbmp 
= new wxBitmap(collapse_xpm
); 
 425     // calculate average font height for bitmap centering 
 427     m_iconWidth 
= m_expandbmp
->GetWidth(); 
 428     m_iconHeight 
= m_expandbmp
->GetHeight(); 
 431     m_curcursor 
= wxCURSOR_ARROW
; 
 432     m_cursorSizeWE 
= new wxCursor( wxCURSOR_SIZEWE 
); 
 434     // adjust bitmap icon y position so they are centered 
 435     m_vspacing 
= wxPG_DEFAULT_VSPACING
; 
 437     CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING 
); 
 439     // Allocate cell datas indirectly by calling setter 
 440     m_propertyDefaultCell
.SetBgCol(*wxBLACK
); 
 441     m_categoryDefaultCell
.SetBgCol(*wxBLACK
); 
 445     // This helps with flicker 
 446     SetBackgroundStyle( wxBG_STYLE_CUSTOM 
); 
 448     // Hook the top-level parent 
 453     // set virtual size to this window size 
 454     wxSize wndsize 
= GetSize(); 
 455     SetVirtualSize(wndsize
.GetWidth(), wndsize
.GetWidth()); 
 457     m_timeCreated 
= ::wxGetLocalTimeMillis(); 
 459     //m_canvas->Create(this, wxID_ANY, wxPoint(0, 0), GetClientSize(), 
 460     //                 wxWANTS_CHARS | wxCLIP_CHILDREN); 
 461     SetBackgroundStyle( wxBG_STYLE_CUSTOM 
); 
 463     m_iFlags 
|= wxPG_FL_INITIALIZED
; 
 465     m_ncWidth 
= wndsize
.GetWidth(); 
 467     // Need to call OnResize handler or size given in constructor/Create 
 469     wxSizeEvent 
sizeEvent(wndsize
,0); 
 473 // ----------------------------------------------------------------------- 
 475 wxPropertyGrid::~wxPropertyGrid() 
 480     wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
 484     // Remove grid and property pointers from live wxPropertyGridEvents. 
 485     for ( i
=0; i
<m_liveEvents
.size(); i
++ ) 
 487         wxPropertyGridEvent
* evt 
= m_liveEvents
[i
]; 
 488         evt
->SetPropertyGrid(NULL
); 
 489         evt
->SetProperty(NULL
); 
 491     m_liveEvents
.clear(); 
 493     if ( m_processedEvent 
) 
 495         // All right... we are being deleted while wxPropertyGrid event 
 496         // is being sent. Make sure that event propagates as little 
 497         // as possible (although usually this is not enough to prevent 
 499         m_processedEvent
->Skip(false); 
 500         m_processedEvent
->StopPropagation(); 
 502         // Let's use wxMessageBox to make the message appear more 
 503         // reliably (and *before* the crash can happen). 
 504         ::wxMessageBox("wxPropertyGrid was being destroyed in an event " 
 505                        "generated by it. This usually leads to a crash " 
 506                        "so it is recommended to destroy the control " 
 507                        "at idle time instead."); 
 510     DoSelectProperty(NULL
, wxPG_SEL_NOVALIDATE
|wxPG_SEL_DONT_SEND_EVENT
); 
 512     // This should do prevent things from going too badly wrong 
 513     m_iFlags 
&= ~(wxPG_FL_INITIALIZED
); 
 515     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
 518     // Call with NULL to disconnect event handling 
 519     if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING 
) 
 523         wxASSERT_MSG( !IsEditorsValueModified(), 
 524                       wxS("Most recent change in property editor was ") 
 525                       wxS("lost!!! (if you don't want this to happen, ") 
 526                       wxS("close your frames and dialogs using ") 
 527                       wxS("Close(false).)") ); 
 530 #if wxPG_DOUBLE_BUFFER 
 531     if ( m_doubleBuffer 
) 
 532         delete m_doubleBuffer
; 
 535     if ( m_iFlags 
& wxPG_FL_CREATEDSTATE 
) 
 538     delete m_cursorSizeWE
; 
 540 #ifndef wxPG_ICON_WIDTH 
 545     // Delete common value records 
 546     for ( i
=0; i
<m_commonValues
.size(); i
++ ) 
 548         // Use temporary variable to work around possible strange VC6 (asserts because m_size is zero) 
 549         wxPGCommonValue
* value 
= m_commonValues
[i
]; 
 554 // ----------------------------------------------------------------------- 
 556 bool wxPropertyGrid::Destroy() 
 558     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
 561     return wxScrolledWindow::Destroy(); 
 564 // ----------------------------------------------------------------------- 
 566 wxPropertyGridPageState
* wxPropertyGrid::CreateState() const 
 568     return new wxPropertyGridPageState(); 
 571 // ----------------------------------------------------------------------- 
 572 // wxPropertyGrid overridden wxWindow methods 
 573 // ----------------------------------------------------------------------- 
 575 void wxPropertyGrid::SetWindowStyleFlag( long style 
) 
 577     long old_style 
= m_windowStyle
; 
 579     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
 581         wxASSERT( m_pState 
); 
 583         if ( !(style 
& wxPG_HIDE_CATEGORIES
) && (old_style 
& wxPG_HIDE_CATEGORIES
) ) 
 586             EnableCategories( true ); 
 588         else if ( (style 
& wxPG_HIDE_CATEGORIES
) && !(old_style 
& wxPG_HIDE_CATEGORIES
) ) 
 590         // Disable categories 
 591             EnableCategories( false ); 
 593         if ( !(old_style 
& wxPG_AUTO_SORT
) && (style 
& wxPG_AUTO_SORT
) ) 
 599                 PrepareAfterItemsAdded(); 
 601                 m_pState
->m_itemsAdded 
= 1; 
 603     #if wxPG_SUPPORT_TOOLTIPS 
 604         if ( !(old_style 
& wxPG_TOOLTIPS
) && (style 
& wxPG_TOOLTIPS
) ) 
 610             wxToolTip* tooltip = new wxToolTip ( wxEmptyString ); 
 611             SetToolTip ( tooltip ); 
 612             tooltip->SetDelay ( wxPG_TOOLTIP_DELAY ); 
 615         else if ( (old_style 
& wxPG_TOOLTIPS
) && !(style 
& wxPG_TOOLTIPS
) ) 
 620             wxScrolledWindow::SetToolTip( NULL 
); 
 625     wxScrolledWindow::SetWindowStyleFlag ( style 
); 
 627     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
 629         if ( (old_style 
& wxPG_HIDE_MARGIN
) != (style 
& wxPG_HIDE_MARGIN
) ) 
 631             CalculateFontAndBitmapStuff( m_vspacing 
); 
 637 // ----------------------------------------------------------------------- 
 639 void wxPropertyGrid::Freeze() 
 643         wxScrolledWindow::Freeze(); 
 648 // ----------------------------------------------------------------------- 
 650 void wxPropertyGrid::Thaw() 
 656         wxScrolledWindow::Thaw(); 
 657         RecalculateVirtualSize(); 
 658     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
 662         // Force property re-selection 
 663         // NB: We must copy the selection. 
 664         wxArrayPGProperty selection 
= m_pState
->m_selection
; 
 665         DoSetSelection(selection
, wxPG_SEL_FORCE 
| wxPG_SEL_NONVISIBLE
); 
 669 // ----------------------------------------------------------------------- 
 671 bool wxPropertyGrid::DoAddToSelection( wxPGProperty
* prop
, int selFlags 
) 
 673     wxCHECK( prop
, false ); 
 675     if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION
) ) 
 676         return DoSelectProperty(prop
, selFlags
); 
 678     wxArrayPGProperty
& selection 
= m_pState
->m_selection
; 
 680     if ( !selection
.size() ) 
 682         return DoSelectProperty(prop
, selFlags
); 
 686         // For categories, only one can be selected at a time 
 687         if ( prop
->IsCategory() || selection
[0]->IsCategory() ) 
 690         selection
.push_back(prop
); 
 692         if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 694             SendEvent( wxEVT_PG_SELECTED
, prop
, NULL 
); 
 703 // ----------------------------------------------------------------------- 
 705 bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty
* prop
, int selFlags 
) 
 707     wxCHECK( prop
, false ); 
 710     wxArrayPGProperty
& selection 
= m_pState
->m_selection
; 
 711     if ( selection
.size() <= 1 ) 
 713         res 
= DoSelectProperty(NULL
, selFlags
); 
 717         m_pState
->DoRemoveFromSelection(prop
); 
 725 // ----------------------------------------------------------------------- 
 727 bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty
* prop
, 
 728                                       unsigned int colIndex
, 
 729                                       unsigned int selFlags 
) 
 732     // NB: Enable following if label editor background colour is 
 733     //     ever changed to any other than m_colSelBack. 
 735     // We use this workaround to prevent visible flicker when editing 
 736     // a cell. Atleast on wxMSW, there is a difficult to find 
 737     // (and perhaps prevent) redraw somewhere between making property 
 738     // selected and enabling label editing. 
 740     //wxColour prevColSelBack = m_colSelBack; 
 741     //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); 
 747         res 
= DoSelectProperty(prop
, selFlags
); 
 752         DoClearSelection(false, wxPG_SEL_NO_REFRESH
); 
 754         if ( m_pState
->m_editableColumns
.Index(colIndex
) == wxNOT_FOUND 
) 
 756             res 
= DoAddToSelection(prop
, selFlags
); 
 760             res 
= DoAddToSelection(prop
, selFlags
|wxPG_SEL_NO_REFRESH
); 
 762             DoBeginLabelEdit(colIndex
, selFlags
); 
 766     //m_colSelBack = prevColSelBack; 
 770 // ----------------------------------------------------------------------- 
 772 bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty
* prop
, 
 773                                                    unsigned int colIndex
, 
 774                                                    wxMouseEvent
* mouseEvent
, 
 777     bool alreadySelected 
= m_pState
->DoIsPropertySelected(prop
); 
 779     bool addToExistingSelection
; 
 781     if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION 
) 
 785             if ( mouseEvent
->GetEventType() == wxEVT_RIGHT_DOWN 
|| 
 786                  mouseEvent
->GetEventType() == wxEVT_RIGHT_UP 
) 
 788                 // Allow right-click for context menu without 
 789                 // disturbing the selection. 
 790                 if ( GetSelectedProperties().size() <= 1 || 
 792                     return DoSelectAndEdit(prop
, colIndex
, selFlags
); 
 797                 addToExistingSelection 
= mouseEvent
->ShiftDown(); 
 802             addToExistingSelection 
= false; 
 807         addToExistingSelection 
= false; 
 810     if ( addToExistingSelection 
) 
 812         if ( !alreadySelected 
) 
 814             res 
= DoAddToSelection(prop
, selFlags
); 
 816         else if ( GetSelectedProperties().size() > 1 ) 
 818             res 
= DoRemoveFromSelection(prop
, selFlags
); 
 823         res 
= DoSelectAndEdit(prop
, colIndex
, selFlags
); 
 829 // ----------------------------------------------------------------------- 
 831 void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty
& newSelection
, 
 834     if ( newSelection
.size() > 0 ) 
 836         if ( !DoSelectProperty(newSelection
[0], selFlags
) ) 
 841         DoClearSelection(false, selFlags
); 
 844     for ( unsigned int i 
= 1; i 
< newSelection
.size(); i
++ ) 
 846         DoAddToSelection(newSelection
[i
], selFlags
); 
 852 // ----------------------------------------------------------------------- 
 854 void wxPropertyGrid::MakeColumnEditable( unsigned int column
, 
 857     wxASSERT( column 
!= 1 ); 
 859     wxArrayInt
& cols 
= m_pState
->m_editableColumns
; 
 863         cols
.push_back(column
); 
 867         for ( int i 
= cols
.size() - 1; i 
> 0; i
-- ) 
 869             if ( cols
[i
] == (int)column 
) 
 870                 cols
.erase( cols
.begin() + i 
); 
 875 // ----------------------------------------------------------------------- 
 877 void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex
, 
 880     wxPGProperty
* selected 
= GetSelection(); 
 881     wxCHECK_RET(selected
, wxT("No property selected")); 
 882     wxCHECK_RET(colIndex 
!= 1, wxT("Do not use this for column 1")); 
 884     if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 886         if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN
, 
 893     const wxPGCell
* cell 
= NULL
; 
 894     if ( selected
->HasCell(colIndex
) ) 
 896         cell 
= &selected
->GetCell(colIndex
); 
 897         if ( !cell
->HasText() && colIndex 
== 0 ) 
 898             text 
= selected
->GetLabel(); 
 904             text 
= selected
->GetLabel(); 
 906             cell 
= &selected
->GetOrCreateCell(colIndex
); 
 909     if ( cell 
&& cell
->HasText() ) 
 910         text 
= cell
->GetText(); 
 912     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
);  // send event 
 914     m_selColumn 
= colIndex
; 
 916     wxRect r 
= GetEditorWidgetRect(selected
, m_selColumn
); 
 918     wxWindow
* tc 
= GenerateEditorTextCtrl(r
.GetPosition(), 
 926     wxWindowID id 
= tc
->GetId(); 
 927     tc
->Connect(id
, wxEVT_COMMAND_TEXT_ENTER
, 
 928         wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress
), 
 930     tc
->Connect(id
, wxEVT_KEY_DOWN
, 
 931         wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress
), 
 936     m_labelEditor 
= wxStaticCast(tc
, wxTextCtrl
); 
 937     m_labelEditorProperty 
= selected
; 
 940 // ----------------------------------------------------------------------- 
 943 wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent
& WXUNUSED(event
) ) 
 945     DoEndLabelEdit(true); 
 948 // ----------------------------------------------------------------------- 
 950 void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent
& event 
) 
 952     int keycode 
= event
.GetKeyCode(); 
 954     if ( keycode 
== WXK_ESCAPE 
) 
 956         DoEndLabelEdit(false); 
 964 // ----------------------------------------------------------------------- 
 966 void wxPropertyGrid::DoEndLabelEdit( bool commit
, int selFlags 
) 
 968     if ( !m_labelEditor 
) 
 971     wxPGProperty
* prop 
= m_labelEditorProperty
; 
 976         if ( !(selFlags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
 978             // wxPG_SEL_NOVALIDATE is passed correctly in selFlags 
 979             if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING
, 
 980                             prop
, NULL
, selFlags
, 
 985         wxString text 
= m_labelEditor
->GetValue(); 
 986         wxPGCell
* cell 
= NULL
; 
 987         if ( prop
->HasCell(m_selColumn
) ) 
 989             cell 
= &prop
->GetCell(m_selColumn
); 
 993             if ( m_selColumn 
== 0 ) 
 994                 prop
->SetLabel(text
); 
 996                 cell 
= &prop
->GetOrCreateCell(m_selColumn
); 
1000             cell
->SetText(text
); 
1005     DestroyEditorWnd(m_labelEditor
); 
1006     m_labelEditor 
= NULL
; 
1007     m_labelEditorProperty 
= NULL
; 
1012 // ----------------------------------------------------------------------- 
1014 void wxPropertyGrid::SetExtraStyle( long exStyle 
) 
1016     if ( exStyle 
& wxPG_EX_ENABLE_TLP_TRACKING 
) 
1017         OnTLPChanging(::wxGetTopLevelParent(this)); 
1019         OnTLPChanging(NULL
); 
1021     if ( exStyle 
& wxPG_EX_NATIVE_DOUBLE_BUFFERING 
) 
1023 #if defined(__WXMSW__) 
1026         // Don't use WS_EX_COMPOSITED just now. 
1029         if ( m_iFlags & wxPG_FL_IN_MANAGER ) 
1030             hWnd = (HWND)GetParent()->GetHWND(); 
1032             hWnd = (HWND)GetHWND(); 
1034         ::SetWindowLong( hWnd, GWL_EXSTYLE, 
1035                          ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED ); 
1038 //#elif defined(__WXGTK20__) 
1040         // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window 
1041         // truly was double-buffered. 
1042         if ( !this->IsDoubleBuffered() ) 
1044             exStyle 
&= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING
); 
1048         #if wxPG_DOUBLE_BUFFER 
1049             delete m_doubleBuffer
; 
1050             m_doubleBuffer 
= NULL
; 
1055     wxScrolledWindow::SetExtraStyle( exStyle 
); 
1057     if ( exStyle 
& wxPG_EX_INIT_NOCAT 
) 
1058         m_pState
->InitNonCatMode(); 
1060     if ( exStyle 
& wxPG_EX_HELP_AS_TOOLTIPS 
) 
1061         m_windowStyle 
|= wxPG_TOOLTIPS
; 
1064     wxPGGlobalVars
->m_extraStyle 
= exStyle
; 
1067 // ----------------------------------------------------------------------- 
1069 // returns the best acceptable minimal size 
1070 wxSize 
wxPropertyGrid::DoGetBestSize() const 
1072     int lineHeight 
= wxMax(15, m_lineHeight
); 
1074     // don't make the grid too tall (limit height to 10 items) but don't 
1075     // make it too small neither 
1076     int numLines 
= wxMin
 
1078                     wxMax(m_pState
->m_properties
->GetChildCount(), 3), 
1082     wxClientDC 
dc(const_cast<wxPropertyGrid 
*>(this)); 
1083     int width 
= m_marginWidth
; 
1084     for ( unsigned int i 
= 0; i 
< m_pState
->m_colWidths
.size(); i
++ ) 
1086         width 
+= m_pState
->GetColumnFitWidth(dc
, m_pState
->DoGetRoot(), i
, true); 
1089     const wxSize sz 
= wxSize(width
, lineHeight
*numLines 
+ 40); 
1095 // ----------------------------------------------------------------------- 
1097 void wxPropertyGrid::OnTLPChanging( wxWindow
* newTLP 
) 
1099     if ( newTLP 
== m_tlp 
) 
1102     wxLongLong currentTime 
= ::wxGetLocalTimeMillis(); 
1105     // Parent changed so let's redetermine and re-hook the 
1106     // correct top-level window. 
1109         m_tlp
->Disconnect( wxEVT_CLOSE_WINDOW
, 
1110                            wxCloseEventHandler(wxPropertyGrid::OnTLPClose
), 
1112         m_tlpClosed 
= m_tlp
; 
1113         m_tlpClosedTime 
= currentTime
; 
1118         // Only accept new tlp if same one was not just dismissed. 
1119         if ( newTLP 
!= m_tlpClosed 
|| 
1120              m_tlpClosedTime
+250 < currentTime 
) 
1122             newTLP
->Connect( wxEVT_CLOSE_WINDOW
, 
1123                              wxCloseEventHandler(wxPropertyGrid::OnTLPClose
), 
1136 // ----------------------------------------------------------------------- 
1138 void wxPropertyGrid::OnTLPClose( wxCloseEvent
& event 
) 
1140     // ClearSelection forces value validation/commit. 
1141     if ( event
.CanVeto() && !DoClearSelection() ) 
1147     // Ok, it can close, set tlp pointer to NULL. Some other event 
1148     // handler can of course veto the close, but our OnIdle() should 
1149     // then be able to regain the tlp pointer. 
1150     OnTLPChanging(NULL
); 
1155 // ----------------------------------------------------------------------- 
1157 bool wxPropertyGrid::Reparent( wxWindowBase 
*newParent 
) 
1159     OnTLPChanging((wxWindow
*)newParent
); 
1161     bool res 
= wxScrolledWindow::Reparent(newParent
); 
1166 // ----------------------------------------------------------------------- 
1167 // wxPropertyGrid Font and Colour Methods 
1168 // ----------------------------------------------------------------------- 
1170 void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing 
) 
1174     m_captionFont 
= wxScrolledWindow::GetFont(); 
1176     GetTextExtent(wxS("jG"), &x
, &y
, 0, 0, &m_captionFont
); 
1177     m_subgroup_extramargin 
= x 
+ (x
/2); 
1180 #if wxPG_USE_RENDERER_NATIVE 
1181     m_iconWidth 
= wxPG_ICON_WIDTH
; 
1182 #elif wxPG_ICON_WIDTH 
1184     m_iconWidth 
= (m_fontHeight 
* wxPG_ICON_WIDTH
) / 13; 
1185     if ( m_iconWidth 
< 5 ) m_iconWidth 
= 5; 
1186     else if ( !(m_iconWidth 
& 0x01) ) m_iconWidth
++; // must be odd 
1190     m_gutterWidth 
= m_iconWidth 
/ wxPG_GUTTER_DIV
; 
1191     if ( m_gutterWidth 
< wxPG_GUTTER_MIN 
) 
1192         m_gutterWidth 
= wxPG_GUTTER_MIN
; 
1195     if ( vspacing 
<= 1 ) vdiv 
= 12; 
1196     else if ( vspacing 
>= 3 ) vdiv 
= 3; 
1198     m_spacingy 
= m_fontHeight 
/ vdiv
; 
1199     if ( m_spacingy 
< wxPG_YSPACING_MIN 
) 
1200         m_spacingy 
= wxPG_YSPACING_MIN
; 
1203     if ( !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
1204         m_marginWidth 
= m_gutterWidth
*2 + m_iconWidth
; 
1206     m_captionFont
.SetWeight(wxBOLD
); 
1207     GetTextExtent(wxS("jG"), &x
, &y
, 0, 0, &m_captionFont
); 
1209     m_lineHeight 
= m_fontHeight
+(2*m_spacingy
)+1; 
1212     m_buttonSpacingY 
= (m_lineHeight 
- m_iconHeight
) / 2; 
1213     if ( m_buttonSpacingY 
< 0 ) m_buttonSpacingY 
= 0; 
1216         m_pState
->CalculateFontAndBitmapStuff(vspacing
); 
1218     if ( m_iFlags 
& wxPG_FL_INITIALIZED 
) 
1219         RecalculateVirtualSize(); 
1221     InvalidateBestSize(); 
1224 // ----------------------------------------------------------------------- 
1226 void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent 
&WXUNUSED(event
) ) 
1232 // ----------------------------------------------------------------------- 
1234 static wxColour 
wxPGAdjustColour(const wxColour
& src
, int ra
, 
1235                                  int ga 
= 1000, int ba 
= 1000, 
1236                                  bool forceDifferent 
= false) 
1243     // Recursion guard (allow 2 max) 
1244     static int isinside 
= 0; 
1246     wxCHECK_MSG( isinside 
< 3, 
1248                  wxT("wxPGAdjustColour should not be recursively called more than once") ); 
1253     int g 
= src
.Green(); 
1256     if ( r2
>255 ) r2 
= 255; 
1257     else if ( r2
<0) r2 
= 0; 
1259     if ( g2
>255 ) g2 
= 255; 
1260     else if ( g2
<0) g2 
= 0; 
1262     if ( b2
>255 ) b2 
= 255; 
1263     else if ( b2
<0) b2 
= 0; 
1265     // Make sure they are somewhat different 
1266     if ( forceDifferent 
&& (abs((r
+g
+b
)-(r2
+g2
+b2
)) < abs(ra
/2)) ) 
1267         dst 
= wxPGAdjustColour(src
,-(ra
*2)); 
1269         dst 
= wxColour(r2
,g2
,b2
); 
1271     // Recursion guard (allow 2 max) 
1278 static int wxPGGetColAvg( const wxColour
& col 
) 
1280     return (col
.Red() + col
.Green() + col
.Blue()) / 3; 
1284 void wxPropertyGrid::RegainColours() 
1286     if ( !(m_coloursCustomized 
& 0x0002) ) 
1288         wxColour col 
= wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE 
); 
1290         // Make sure colour is dark enough 
1292         int colDec 
= wxPGGetColAvg(col
) - 230; 
1294         int colDec 
= wxPGGetColAvg(col
) - 200; 
1297             m_colCapBack 
= wxPGAdjustColour(col
,-colDec
); 
1300         m_categoryDefaultCell
.GetData()->SetBgCol(m_colCapBack
); 
1303     if ( !(m_coloursCustomized 
& 0x0001) ) 
1304         m_colMargin 
= m_colCapBack
; 
1306     if ( !(m_coloursCustomized 
& 0x0004) ) 
1313         wxColour capForeCol 
= wxPGAdjustColour(m_colCapBack
,colDec
,5000,5000,true); 
1314         m_colCapFore 
= capForeCol
; 
1315         m_categoryDefaultCell
.GetData()->SetFgCol(capForeCol
); 
1318     if ( !(m_coloursCustomized 
& 0x0008) ) 
1320         wxColour bgCol 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW 
); 
1321         m_colPropBack 
= bgCol
; 
1322         m_propertyDefaultCell
.GetData()->SetBgCol(bgCol
); 
1325     if ( !(m_coloursCustomized 
& 0x0010) ) 
1327         wxColour fgCol 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT 
); 
1328         m_colPropFore 
= fgCol
; 
1329         m_propertyDefaultCell
.GetData()->SetFgCol(fgCol
); 
1332     if ( !(m_coloursCustomized 
& 0x0020) ) 
1333         m_colSelBack 
= wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT 
); 
1335     if ( !(m_coloursCustomized 
& 0x0040) ) 
1336         m_colSelFore 
= wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT 
); 
1338     if ( !(m_coloursCustomized 
& 0x0080) ) 
1339         m_colLine 
= m_colCapBack
; 
1341     if ( !(m_coloursCustomized 
& 0x0100) ) 
1342         m_colDisPropFore 
= m_colCapFore
; 
1344     m_colEmptySpace 
= wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW 
); 
1347 // ----------------------------------------------------------------------- 
1349 void wxPropertyGrid::ResetColours() 
1351     m_coloursCustomized 
= 0; 
1358 // ----------------------------------------------------------------------- 
1360 bool wxPropertyGrid::SetFont( const wxFont
& font 
) 
1362     // Must disable active editor. 
1365     bool res 
= wxScrolledWindow::SetFont( font 
); 
1366     if ( res 
&& GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant 
1368         CalculateFontAndBitmapStuff( m_vspacing 
); 
1375 // ----------------------------------------------------------------------- 
1377 void wxPropertyGrid::SetLineColour( const wxColour
& col 
) 
1380     m_coloursCustomized 
|= 0x80; 
1384 // ----------------------------------------------------------------------- 
1386 void wxPropertyGrid::SetMarginColour( const wxColour
& col 
) 
1389     m_coloursCustomized 
|= 0x01; 
1393 // ----------------------------------------------------------------------- 
1395 void wxPropertyGrid::SetCellBackgroundColour( const wxColour
& col 
) 
1397     m_colPropBack 
= col
; 
1398     m_coloursCustomized 
|= 0x08; 
1400     m_propertyDefaultCell
.GetData()->SetBgCol(col
); 
1405 // ----------------------------------------------------------------------- 
1407 void wxPropertyGrid::SetCellTextColour( const wxColour
& col 
) 
1409     m_colPropFore 
= col
; 
1410     m_coloursCustomized 
|= 0x10; 
1412     m_propertyDefaultCell
.GetData()->SetFgCol(col
); 
1417 // ----------------------------------------------------------------------- 
1419 void wxPropertyGrid::SetEmptySpaceColour( const wxColour
& col 
) 
1421     m_colEmptySpace 
= col
; 
1426 // ----------------------------------------------------------------------- 
1428 void wxPropertyGrid::SetCellDisabledTextColour( const wxColour
& col 
) 
1430     m_colDisPropFore 
= col
; 
1431     m_coloursCustomized 
|= 0x100; 
1435 // ----------------------------------------------------------------------- 
1437 void wxPropertyGrid::SetSelectionBackgroundColour( const wxColour
& col 
) 
1440     m_coloursCustomized 
|= 0x20; 
1444 // ----------------------------------------------------------------------- 
1446 void wxPropertyGrid::SetSelectionTextColour( const wxColour
& col 
) 
1449     m_coloursCustomized 
|= 0x40; 
1453 // ----------------------------------------------------------------------- 
1455 void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour
& col 
) 
1458     m_coloursCustomized 
|= 0x02; 
1460     m_categoryDefaultCell
.GetData()->SetBgCol(col
); 
1465 // ----------------------------------------------------------------------- 
1467 void wxPropertyGrid::SetCaptionTextColour( const wxColour
& col 
) 
1470     m_coloursCustomized 
|= 0x04; 
1472     m_categoryDefaultCell
.GetData()->SetFgCol(col
); 
1477 // ----------------------------------------------------------------------- 
1478 // wxPropertyGrid property adding and removal 
1479 // ----------------------------------------------------------------------- 
1481 void wxPropertyGrid::PrepareAfterItemsAdded() 
1483     if ( !m_pState 
|| !m_pState
->m_itemsAdded 
) return; 
1485     m_pState
->m_itemsAdded 
= 0; 
1487     if ( m_windowStyle 
& wxPG_AUTO_SORT 
) 
1488         Sort(wxPG_SORT_TOP_LEVEL_ONLY
); 
1490     RecalculateVirtualSize(); 
1493 // ----------------------------------------------------------------------- 
1494 // wxPropertyGrid property operations 
1495 // ----------------------------------------------------------------------- 
1497 bool wxPropertyGrid::EnsureVisible( wxPGPropArg id 
) 
1499     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
1503     bool changed 
= false; 
1505     // Is it inside collapsed section? 
1506     if ( !p
->IsVisible() ) 
1509         wxPGProperty
* parent 
= p
->GetParent(); 
1510         wxPGProperty
* grandparent 
= parent
->GetParent(); 
1512         if ( grandparent 
&& grandparent 
!= m_pState
->m_properties 
) 
1513             Expand( grandparent 
); 
1521     GetViewStart(&vx
,&vy
); 
1522     vy
*=wxPG_PIXELS_PER_UNIT
; 
1528         Scroll(vx
, y
/wxPG_PIXELS_PER_UNIT 
); 
1529         m_iFlags 
|= wxPG_FL_SCROLLED
; 
1532     else if ( (y
+m_lineHeight
) > (vy
+m_height
) ) 
1534         Scroll(vx
, (y
-m_height
+(m_lineHeight
*2))/wxPG_PIXELS_PER_UNIT 
); 
1535         m_iFlags 
|= wxPG_FL_SCROLLED
; 
1545 // ----------------------------------------------------------------------- 
1546 // wxPropertyGrid helper methods called by properties 
1547 // ----------------------------------------------------------------------- 
1549 // Control font changer helper. 
1550 void wxPropertyGrid::SetCurControlBoldFont() 
1552     wxASSERT( m_wndEditor 
); 
1553     m_wndEditor
->SetFont( m_captionFont 
); 
1556 // ----------------------------------------------------------------------- 
1558 wxPoint 
wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty
* p
, 
1561 #if wxPG_SMALL_SCREEN 
1562     // On small-screen devices, always show dialogs with default position and size. 
1563     return wxDefaultPosition
; 
1565     int splitterX 
= GetSplitterPosition(); 
1569     wxCHECK_MSG( y 
>= 0, wxPoint(-1,-1), wxT("invalid y?") ); 
1571     ImprovedClientToScreen( &x
, &y 
); 
1573     int sw 
= wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X 
); 
1574     int sh 
= wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y 
); 
1581         new_x 
= x 
+ (m_width
-splitterX
) - sz
.x
; 
1591         new_y 
= y 
+ m_lineHeight
; 
1593     return wxPoint(new_x
,new_y
); 
1597 // ----------------------------------------------------------------------- 
1599 wxString
& wxPropertyGrid::ExpandEscapeSequences( wxString
& dst_str
, wxString
& src_str 
) 
1601     if ( src_str
.length() == 0 ) 
1607     bool prev_is_slash 
= false; 
1609     wxString::const_iterator i 
= src_str
.begin(); 
1613     for ( ; i 
!= src_str
.end(); ++i 
) 
1617         if ( a 
!= wxS('\\') ) 
1619             if ( !prev_is_slash 
) 
1625                 if ( a 
== wxS('n') ) 
1628                     dst_str 
<< wxS('\n'); 
1630                     dst_str 
<< wxS('\n'); 
1633                 else if ( a 
== wxS('t') ) 
1634                     dst_str 
<< wxS('\t'); 
1638             prev_is_slash 
= false; 
1642             if ( prev_is_slash 
) 
1644                 dst_str 
<< wxS('\\'); 
1645                 prev_is_slash 
= false; 
1649                 prev_is_slash 
= true; 
1656 // ----------------------------------------------------------------------- 
1658 wxString
& wxPropertyGrid::CreateEscapeSequences( wxString
& dst_str
, wxString
& src_str 
) 
1660     if ( src_str
.length() == 0 ) 
1666     wxString::const_iterator i 
= src_str
.begin(); 
1667     wxUniChar prev_a 
= wxS('\0'); 
1671     for ( ; i 
!= src_str
.end(); ++i 
) 
1675         if ( a 
>= wxS(' ') ) 
1677             // This surely is not something that requires an escape sequence. 
1682             // This might need... 
1683             if ( a 
== wxS('\r')  ) 
1685                 // DOS style line end. 
1686                 // Already taken care below 
1688             else if ( a 
== wxS('\n') ) 
1689                 // UNIX style line end. 
1690                 dst_str 
<< wxS("\\n"); 
1691             else if ( a 
== wxS('\t') ) 
1693                 dst_str 
<< wxS('\t'); 
1696                 //wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a); 
1706 // ----------------------------------------------------------------------- 
1708 wxPGProperty
* wxPropertyGrid::DoGetItemAtY( int y 
) const 
1715     return m_pState
->m_properties
->GetItemAtY(y
, m_lineHeight
, &a
); 
1718 // ----------------------------------------------------------------------- 
1719 // wxPropertyGrid graphics related methods 
1720 // ----------------------------------------------------------------------- 
1722 void wxPropertyGrid::OnPaint( wxPaintEvent
& WXUNUSED(event
) ) 
1727     // Don't paint after destruction has begun 
1728     if ( !HasInternalFlag(wxPG_FL_INITIALIZED
) ) 
1731     // Find out where the window is scrolled to 
1732     int vx
,vy
;                     // Top left corner of client 
1733     GetViewStart(&vx
,&vy
); 
1734     vy 
*= wxPG_PIXELS_PER_UNIT
; 
1736     // Update everything inside the box 
1737     wxRect r 
= GetUpdateRegion().GetBox(); 
1741     // FIXME: This is just a workaround for a bug that causes splitters not 
1742     //        to paint when other windows are being dragged over the grid. 
1743     wxRect fullRect 
= GetRect(); 
1745     r
.width 
= fullRect
.width
; 
1747     // Repaint this rectangle 
1748     DrawItems( dc
, r
.y
, r
.y 
+ r
.height
, &r 
); 
1750     // We assume that the size set when grid is shown 
1751     // is what is desired. 
1752     SetInternalFlag(wxPG_FL_GOOD_SIZE_SET
); 
1755 // ----------------------------------------------------------------------- 
1757 void wxPropertyGrid::DrawExpanderButton( wxDC
& dc
, const wxRect
& rect
, 
1758                                          wxPGProperty
* property 
) const 
1760     // Prepare rectangle to be used 
1762     r
.x 
+= m_gutterWidth
; r
.y 
+= m_buttonSpacingY
; 
1763     r
.width 
= m_iconWidth
; r
.height 
= m_iconHeight
; 
1765 #if (wxPG_USE_RENDERER_NATIVE) 
1767 #elif wxPG_ICON_WIDTH 
1768     // Drawing expand/collapse button manually 
1769     dc
.SetPen(m_colPropFore
); 
1770     if ( property
->IsCategory() ) 
1771         dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
1773         dc
.SetBrush(m_colPropBack
); 
1775     dc
.DrawRectangle( r 
); 
1776     int _y 
= r
.y
+(m_iconWidth
/2); 
1777     dc
.DrawLine(r
.x
+2,_y
,r
.x
+m_iconWidth
-2,_y
); 
1782     if ( property
->IsExpanded() ) 
1784     // wxRenderer functions are non-mutating in nature, so it 
1785     // should be safe to cast "const wxPropertyGrid*" to "wxWindow*". 
1786     // Hopefully this does not cause problems. 
1787     #if (wxPG_USE_RENDERER_NATIVE) 
1788         wxRendererNative::Get().DrawTreeItemButton( 
1794     #elif wxPG_ICON_WIDTH 
1803     #if (wxPG_USE_RENDERER_NATIVE) 
1804         wxRendererNative::Get().DrawTreeItemButton( 
1810     #elif wxPG_ICON_WIDTH 
1811         int _x 
= r
.x
+(m_iconWidth
/2); 
1812         dc
.DrawLine(_x
,r
.y
+2,_x
,r
.y
+m_iconWidth
-2); 
1818 #if (wxPG_USE_RENDERER_NATIVE) 
1820 #elif wxPG_ICON_WIDTH 
1823     dc
.DrawBitmap( *bmp
, r
.x
, r
.y
, true ); 
1827 // ----------------------------------------------------------------------- 
1830 // This is the one called by OnPaint event handler and others. 
1831 // topy and bottomy are already unscrolled (ie. physical) 
1833 void wxPropertyGrid::DrawItems( wxDC
& dc
, 
1834                                 unsigned int topItemY
, 
1835                                 unsigned int bottomItemY
, 
1836                                 const wxRect
* drawRect 
) 
1840          bottomItemY 
< topItemY 
|| 
1844     m_pState
->EnsureVirtualHeight(); 
1846     wxRect tempDrawRect
; 
1849         tempDrawRect 
= wxRect(0, topItemY
, 
1852         drawRect 
= &tempDrawRect
; 
1855     // items added check 
1856     if ( m_pState
->m_itemsAdded 
) PrepareAfterItemsAdded(); 
1858     int paintFinishY 
= 0; 
1860     if ( m_pState
->m_properties
->GetChildCount() > 0 ) 
1863         bool isBuffered 
= false; 
1865     #if wxPG_DOUBLE_BUFFER 
1866         wxMemoryDC
* bufferDC 
= NULL
; 
1868         if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING
) ) 
1870             if ( !m_doubleBuffer 
) 
1872                 paintFinishY 
= drawRect
->y
; 
1877                 bufferDC 
= new wxMemoryDC(); 
1879                 // If nothing was changed, then just copy from double-buffer 
1880                 bufferDC
->SelectObject( *m_doubleBuffer 
); 
1890             dc
.SetClippingRegion( *drawRect 
); 
1891             paintFinishY 
= DoDrawItems( *dcPtr
, drawRect
, isBuffered 
); 
1892             int drawBottomY 
= drawRect
->y 
+ drawRect
->height
; 
1894             // Clear area beyond last painted property 
1895             if ( paintFinishY 
< drawBottomY 
) 
1897                 dcPtr
->SetPen(m_colEmptySpace
); 
1898                 dcPtr
->SetBrush(m_colEmptySpace
); 
1899                 dcPtr
->DrawRectangle(0, paintFinishY
, 
1904             dc
.DestroyClippingRegion(); 
1907     #if wxPG_DOUBLE_BUFFER 
1910             dc
.Blit( drawRect
->x
, drawRect
->y
, drawRect
->width
, 
1912                      bufferDC
, 0, 0, wxCOPY 
); 
1919         // Just clear the area 
1920         dc
.SetPen(m_colEmptySpace
); 
1921         dc
.SetBrush(m_colEmptySpace
); 
1922         dc
.DrawRectangle(*drawRect
); 
1926 // ----------------------------------------------------------------------- 
1928 int wxPropertyGrid::DoDrawItems( wxDC
& dc
, 
1929                                  const wxRect
* drawRect
, 
1930                                  bool isBuffered 
) const 
1932     const wxPGProperty
* firstItem
; 
1933     const wxPGProperty
* lastItem
; 
1935     firstItem 
= DoGetItemAtY(drawRect
->y
); 
1936     lastItem 
= DoGetItemAtY(drawRect
->y
+drawRect
->height
-1); 
1939         lastItem 
= GetLastItem( wxPG_ITERATE_VISIBLE 
); 
1941     if ( m_frozen 
|| m_height 
< 1 || firstItem 
== NULL 
) 
1944     wxCHECK_MSG( !m_pState
->m_itemsAdded
, drawRect
->y
, 
1946     wxASSERT( m_pState
->m_properties
->GetChildCount() ); 
1948     int lh 
= m_lineHeight
; 
1951     int lastItemBottomY
; 
1953     firstItemTopY 
= drawRect
->y
; 
1954     lastItemBottomY 
= drawRect
->y 
+ drawRect
->height
; 
1956     // Align y coordinates to item boundaries 
1957     firstItemTopY 
-= firstItemTopY 
% lh
; 
1958     lastItemBottomY 
+= lh 
- (lastItemBottomY 
% lh
); 
1959     lastItemBottomY 
-= 1; 
1961     // Entire range outside scrolled, visible area? 
1962     if ( firstItemTopY 
>= (int)m_pState
->GetVirtualHeight() || 
1963          lastItemBottomY 
<= 0 ) 
1966     wxCHECK_MSG( firstItemTopY 
< lastItemBottomY
, 
1968                  "invalid y values" ); 
1971     wxLogDebug("  -> DoDrawItems ( \"%s\" -> \"%s\" 
1972                "height=%i (ch=%i), drawRect = 0x%lX )", 
1973         firstItem->GetLabel().c_str(), 
1974         lastItem->GetLabel().c_str(), 
1975         (int)(lastItemBottomY - firstItemTopY), 
1977         (unsigned long)drawRect ); 
1982     long windowStyle 
= m_windowStyle
; 
1987     // With wxPG_DOUBLE_BUFFER, do double buffering 
1988     // - buffer's y = 0, so align drawRect and coordinates to that 
1990 #if wxPG_DOUBLE_BUFFER 
1997         xRelMod 
= drawRect
->x
; 
1998         yRelMod 
= drawRect
->y
; 
2001         // drawRect conversion 
2006         firstItemTopY 
-= yRelMod
; 
2007         lastItemBottomY 
-= yRelMod
; 
2010     wxUnusedVar(isBuffered
); 
2013     int x 
= m_marginWidth 
- xRelMod
; 
2015     wxFont normalFont 
= GetFont(); 
2017     bool reallyFocused 
= (m_iFlags 
& wxPG_FL_FOCUSED
) != 0; 
2019     bool isPgEnabled 
= IsEnabled(); 
2022     // Prepare some pens and brushes that are often changed to. 
2025     wxBrush 
marginBrush(m_colMargin
); 
2026     wxPen 
marginPen(m_colMargin
); 
2027     wxBrush 
capbgbrush(m_colCapBack
,wxSOLID
); 
2028     wxPen 
linepen(m_colLine
,1,wxSOLID
); 
2030     wxColour selBackCol
; 
2032         selBackCol 
= m_colSelBack
; 
2034         selBackCol 
= m_colMargin
; 
2036     // pen that has same colour as text 
2037     wxPen 
outlinepen(m_colPropFore
,1,wxSOLID
); 
2040     // Clear margin with background colour 
2042     dc
.SetBrush( marginBrush 
); 
2043     if ( !(windowStyle 
& wxPG_HIDE_MARGIN
) ) 
2045         dc
.SetPen( *wxTRANSPARENT_PEN 
); 
2046         dc
.DrawRectangle(-1-xRelMod
,firstItemTopY
-1,x
+2,lastItemBottomY
-firstItemTopY
+2); 
2049     const wxPGProperty
* firstSelected 
= GetSelection(); 
2050     const wxPropertyGridPageState
* state 
= m_pState
; 
2052 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2053     bool wasSelectedPainted 
= false; 
2056     // TODO: Only render columns that are within clipping region. 
2058     dc
.SetFont(normalFont
); 
2060     wxPropertyGridConstIterator 
it( state
, wxPG_ITERATE_VISIBLE
, firstItem 
); 
2061     int endScanBottomY 
= lastItemBottomY 
+ lh
; 
2062     int y 
= firstItemTopY
; 
2065     // Pregenerate list of visible properties. 
2066     wxArrayPGProperty visPropArray
; 
2067     visPropArray
.reserve((m_height
/m_lineHeight
)+6); 
2069     for ( ; !it
.AtEnd(); it
.Next() ) 
2071         const wxPGProperty
* p 
= *it
; 
2073         if ( !p
->HasFlag(wxPG_PROP_HIDDEN
) ) 
2075             visPropArray
.push_back((wxPGProperty
*)p
); 
2077             if ( y 
> endScanBottomY 
) 
2084     visPropArray
.push_back(NULL
); 
2086     wxPGProperty
* nextP 
= visPropArray
[0]; 
2088     int gridWidth 
= state
->m_width
; 
2091     for ( unsigned int arrInd
=1; 
2092           nextP 
&& y 
<= lastItemBottomY
; 
2095         wxPGProperty
* p 
= nextP
; 
2096         nextP 
= visPropArray
[arrInd
]; 
2098         int rowHeight 
= m_fontHeight
+(m_spacingy
*2)+1; 
2099         int textMarginHere 
= x
; 
2100         int renderFlags 
= 0; 
2102         int greyDepth 
= m_marginWidth
; 
2103         if ( !(windowStyle 
& wxPG_HIDE_CATEGORIES
) ) 
2104             greyDepth 
= (((int)p
->m_depthBgCol
)-1) * m_subgroup_extramargin 
+ m_marginWidth
; 
2106         int greyDepthX 
= greyDepth 
- xRelMod
; 
2108         // Use basic depth if in non-categoric mode and parent is base array. 
2109         if ( !(windowStyle 
& wxPG_HIDE_CATEGORIES
) || p
->GetParent() != m_pState
->m_properties 
) 
2111             textMarginHere 
+= ((unsigned int)((p
->m_depth
-1)*m_subgroup_extramargin
)); 
2114         // Paint margin area 
2115         dc
.SetBrush(marginBrush
); 
2116         dc
.SetPen(marginPen
); 
2117         dc
.DrawRectangle( -xRelMod
, y
, greyDepth
, lh 
); 
2119         dc
.SetPen( linepen 
); 
2125         // Modified by JACS to not draw a margin if wxPG_HIDE_MARGIN is specified, since it 
2126         // looks better, at least under Windows when we have a themed border (the themed-window-specific 
2127         // whitespace between the real border and the propgrid margin exacerbates the double-border look). 
2129         // Is this or its parent themed? 
2130         bool suppressMarginEdge 
= (GetWindowStyle() & wxPG_HIDE_MARGIN
) && 
2131             (((GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_THEME
) || 
2132             (((GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_NONE
) && ((GetParent()->GetWindowStyle() & wxBORDER_MASK
) == wxBORDER_THEME
))); 
2134         bool suppressMarginEdge 
= false; 
2136         if (!suppressMarginEdge
) 
2137             dc
.DrawLine( greyDepthX
, y
, greyDepthX
, y2 
); 
2140             // Blank out the margin edge 
2141             dc
.SetPen(wxPen(GetBackgroundColour())); 
2142             dc
.DrawLine( greyDepthX
, y
, greyDepthX
, y2 
); 
2143             dc
.SetPen( linepen 
); 
2150         for ( si
=0; si
<state
->m_colWidths
.size(); si
++ ) 
2152             sx 
+= state
->m_colWidths
[si
]; 
2153             dc
.DrawLine( sx
, y
, sx
, y2 
); 
2156         // Horizontal Line, below 
2157         //   (not if both this and next is category caption) 
2158         if ( p
->IsCategory() && 
2159              nextP 
&& nextP
->IsCategory() ) 
2160             dc
.SetPen(m_colCapBack
); 
2162         dc
.DrawLine( greyDepthX
, y2
-1, gridWidth
-xRelMod
, y2
-1 ); 
2165         // Need to override row colours? 
2169         bool isSelected 
= state
->DoIsPropertySelected(p
); 
2173             // Disabled may get different colour. 
2174             if ( !p
->IsEnabled() ) 
2176                 renderFlags 
|= wxPGCellRenderer::Disabled 
| 
2177                                wxPGCellRenderer::DontUseCellFgCol
; 
2178                 rowFgCol 
= m_colDisPropFore
; 
2183 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2184             if ( p 
== firstSelected 
) 
2185                 wasSelectedPainted 
= true; 
2188             renderFlags 
|= wxPGCellRenderer::Selected
; 
2190             if ( !p
->IsCategory() ) 
2192                 renderFlags 
|= wxPGCellRenderer::DontUseCellFgCol 
| 
2193                                wxPGCellRenderer::DontUseCellBgCol
; 
2195                 if ( reallyFocused 
&& p 
== firstSelected 
) 
2197                     rowFgCol 
= m_colSelFore
; 
2198                     rowBgCol 
= selBackCol
; 
2200                 else if ( isPgEnabled 
) 
2202                     rowFgCol 
= m_colPropFore
; 
2203                     if ( p 
== firstSelected 
) 
2204                         rowBgCol 
= m_colMargin
; 
2206                         rowBgCol 
= selBackCol
; 
2210                     rowFgCol 
= m_colDisPropFore
; 
2211                     rowBgCol 
= selBackCol
; 
2218         if ( rowBgCol
.IsOk() ) 
2219             rowBgBrush 
= wxBrush(rowBgCol
); 
2221         if ( HasInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
) ) 
2222             renderFlags 
= renderFlags 
& ~wxPGCellRenderer::DontUseCellColours
; 
2225         // Fill additional margin area with background colour of first cell 
2226         if ( greyDepthX 
< textMarginHere 
) 
2228             if ( !(renderFlags 
& wxPGCellRenderer::DontUseCellBgCol
) ) 
2230                 wxPGCell
& cell 
= p
->GetCell(0); 
2231                 rowBgCol 
= cell
.GetBgCol(); 
2232                 rowBgBrush 
= wxBrush(rowBgCol
); 
2234             dc
.SetBrush(rowBgBrush
); 
2235             dc
.SetPen(rowBgCol
); 
2236             dc
.DrawRectangle(greyDepthX
+1, y
, 
2237                              textMarginHere
-greyDepthX
, lh
-1); 
2240         bool fontChanged 
= false; 
2242         // Expander button rectangle 
2243         wxRect 
butRect( ((p
->m_depth 
- 1) * m_subgroup_extramargin
) - xRelMod
, 
2248         if ( p
->IsCategory() ) 
2250             // Captions have their cell areas merged as one 
2251             dc
.SetFont(m_captionFont
); 
2253             wxRect 
cellRect(greyDepthX
, y
, gridWidth 
- greyDepth 
+ 2, rowHeight
-1 ); 
2255             if ( renderFlags 
& wxPGCellRenderer::DontUseCellBgCol 
) 
2257                 dc
.SetBrush(rowBgBrush
); 
2258                 dc
.SetPen(rowBgCol
); 
2261             if ( renderFlags 
& wxPGCellRenderer::DontUseCellFgCol 
) 
2263                 dc
.SetTextForeground(rowFgCol
); 
2266             wxPGCellRenderer
* renderer 
= p
->GetCellRenderer(0); 
2267             renderer
->Render( dc
, cellRect
, this, p
, 0, -1, renderFlags 
); 
2270             if ( !HasFlag(wxPG_HIDE_MARGIN
) && p
->HasVisibleChildren() ) 
2271                 DrawExpanderButton( dc
, butRect
, p 
); 
2275             if ( p
->m_flags 
& wxPG_PROP_MODIFIED 
&& (windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
2277                 dc
.SetFont(m_captionFont
); 
2283             int nextCellWidth 
= state
->m_colWidths
[0] - 
2284                                 (greyDepthX 
- m_marginWidth
); 
2285             wxRect 
cellRect(greyDepthX
+1, y
, 0, rowHeight
-1); 
2286             int textXAdd 
= textMarginHere 
- greyDepthX
; 
2288             for ( ci
=0; ci
<state
->m_colWidths
.size(); ci
++ ) 
2290                 cellRect
.width 
= nextCellWidth 
- 1; 
2292                 wxWindow
* cellEditor 
= NULL
; 
2293                 int cellRenderFlags 
= renderFlags
; 
2295                 // Tree Item Button (must be drawn before clipping is set up) 
2296                 if ( ci 
== 0 && !HasFlag(wxPG_HIDE_MARGIN
) && p
->HasVisibleChildren() ) 
2297                     DrawExpanderButton( dc
, butRect
, p 
); 
2300                 if ( isSelected 
&& (ci 
== 1 || ci 
== m_selColumn
) ) 
2302                     if ( p 
== firstSelected 
) 
2304                         if ( ci 
== 1 && m_wndEditor 
) 
2305                             cellEditor 
= m_wndEditor
; 
2306                         else if ( ci 
== m_selColumn 
&& m_labelEditor 
) 
2307                             cellEditor 
= m_labelEditor
; 
2312                         wxColour editorBgCol 
= 
2313                             cellEditor
->GetBackgroundColour(); 
2314                         dc
.SetBrush(editorBgCol
); 
2315                         dc
.SetPen(editorBgCol
); 
2316                         dc
.SetTextForeground(m_colPropFore
); 
2317                         dc
.DrawRectangle(cellRect
); 
2319                         if ( m_dragStatus 
!= 0 || 
2320                              (m_iFlags 
& wxPG_FL_CUR_USES_CUSTOM_IMAGE
) ) 
2325                         dc
.SetBrush(m_colPropBack
); 
2326                         dc
.SetPen(m_colPropBack
); 
2327                         dc
.SetTextForeground(m_colDisPropFore
); 
2328                         if ( p
->IsEnabled() ) 
2329                             dc
.SetTextForeground(rowFgCol
); 
2331                             dc
.SetTextForeground(m_colDisPropFore
); 
2336                     if ( renderFlags 
& wxPGCellRenderer::DontUseCellBgCol 
) 
2338                         dc
.SetBrush(rowBgBrush
); 
2339                         dc
.SetPen(rowBgCol
); 
2342                     if ( renderFlags 
& wxPGCellRenderer::DontUseCellFgCol 
) 
2344                         dc
.SetTextForeground(rowFgCol
); 
2348                 dc
.SetClippingRegion(cellRect
); 
2350                 cellRect
.x 
+= textXAdd
; 
2351                 cellRect
.width 
-= textXAdd
; 
2356                     wxPGCellRenderer
* renderer
; 
2357                     int cmnVal 
= p
->GetCommonValue(); 
2358                     if ( cmnVal 
== -1 || ci 
!= 1 ) 
2360                         renderer 
= p
->GetCellRenderer(ci
); 
2361                         renderer
->Render( dc
, cellRect
, this, p
, ci
, -1, 
2366                         renderer 
= GetCommonValue(cmnVal
)->GetRenderer(); 
2367                         renderer
->Render( dc
, cellRect
, this, p
, ci
, -1, 
2372                 cellX 
+= state
->m_colWidths
[ci
]; 
2373                 if ( ci 
< (state
->m_colWidths
.size()-1) ) 
2374                     nextCellWidth 
= state
->m_colWidths
[ci
+1]; 
2376                 dc
.DestroyClippingRegion(); // Is this really necessary? 
2382             dc
.SetFont(normalFont
); 
2387     // Refresh editor controls (seems not needed on msw) 
2388     // NOTE: This code is mandatory for GTK! 
2389 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2390     if ( wasSelectedPainted 
) 
2393             m_wndEditor
->Refresh(); 
2395             m_wndEditor2
->Refresh(); 
2402 // ----------------------------------------------------------------------- 
2404 wxRect 
wxPropertyGrid::GetPropertyRect( const wxPGProperty
* p1
, const wxPGProperty
* p2 
) const 
2408     if ( m_width 
< 10 || m_height 
< 10 || 
2409          !m_pState
->m_properties
->GetChildCount() || 
2411         return wxRect(0,0,0,0); 
2416     // Return rect which encloses the given property range 
2418     int visTop 
= p1
->GetY(); 
2421         visBottom 
= p2
->GetY() + m_lineHeight
; 
2423         visBottom 
= m_height 
+ visTop
; 
2425     // If seleced property is inside the range, we'll extend the range to include 
2427     wxPGProperty
* selected 
= GetSelection(); 
2430         int selectedY 
= selected
->GetY(); 
2431         if ( selectedY 
>= visTop 
&& selectedY 
< visBottom 
) 
2433             wxWindow
* editor 
= GetEditorControl(); 
2436                 int visBottom2 
= selectedY 
+ editor
->GetSize().y
; 
2437                 if ( visBottom2 
> visBottom 
) 
2438                     visBottom 
= visBottom2
; 
2443     return wxRect(0,visTop
-vy
,m_pState
->m_width
,visBottom
-visTop
); 
2446 // ----------------------------------------------------------------------- 
2448 void wxPropertyGrid::DrawItems( const wxPGProperty
* p1
, const wxPGProperty
* p2 
) 
2453     if ( m_pState
->m_itemsAdded 
) 
2454         PrepareAfterItemsAdded(); 
2456     wxRect r 
= GetPropertyRect(p1
, p2
); 
2463 // ----------------------------------------------------------------------- 
2465 void wxPropertyGrid::RefreshProperty( wxPGProperty
* p 
) 
2467     if ( m_pState
->DoIsPropertySelected(p
) ) 
2469         // NB: We must copy the selection. 
2470         wxArrayPGProperty selection 
= m_pState
->m_selection
; 
2471         DoSetSelection(selection
, wxPG_SEL_FORCE
); 
2474     DrawItemAndChildren(p
); 
2477 // ----------------------------------------------------------------------- 
2479 void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty
* p 
) 
2484     // Draw item, children, and parent too, if it is not category 
2485     wxPGProperty
* parent 
= p
->GetParent(); 
2488             !parent
->IsCategory() && 
2489             parent
->GetParent() ) 
2492          parent 
= parent
->GetParent(); 
2495     DrawItemAndChildren(p
); 
2498 void wxPropertyGrid::DrawItemAndChildren( wxPGProperty
* p 
) 
2500     wxCHECK_RET( p
, wxT("invalid property id") ); 
2502     // Do not draw if in non-visible page 
2503     if ( p
->GetParentState() != m_pState 
) 
2506     // do not draw a single item if multiple pending 
2507     if ( m_pState
->m_itemsAdded 
|| m_frozen 
) 
2510     // Update child control. 
2511     wxPGProperty
* selected 
= GetSelection(); 
2512     if ( selected 
&& selected
->GetParent() == p 
) 
2515     const wxPGProperty
* lastDrawn 
= p
->GetLastVisibleSubItem(); 
2517     DrawItems(p
, lastDrawn
); 
2520 // ----------------------------------------------------------------------- 
2522 void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground
), 
2523                               const wxRect 
*rect 
) 
2525     PrepareAfterItemsAdded(); 
2527     wxWindow::Refresh(false, rect
); 
2529 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
2530     // I think this really helps only GTK+1.2 
2531     if ( m_wndEditor 
) m_wndEditor
->Refresh(); 
2532     if ( m_wndEditor2 
) m_wndEditor2
->Refresh(); 
2536 // ----------------------------------------------------------------------- 
2537 // wxPropertyGrid global operations 
2538 // ----------------------------------------------------------------------- 
2540 void wxPropertyGrid::Clear() 
2542     m_pState
->DoClear(); 
2548     RecalculateVirtualSize(); 
2550     // Need to clear some area at the end 
2552         RefreshRect(wxRect(0, 0, m_width
, m_height
)); 
2555 // ----------------------------------------------------------------------- 
2557 bool wxPropertyGrid::EnableCategories( bool enable 
) 
2564         // Enable categories 
2567         m_windowStyle 
&= ~(wxPG_HIDE_CATEGORIES
); 
2572         // Disable categories 
2574         m_windowStyle 
|= wxPG_HIDE_CATEGORIES
; 
2577     if ( !m_pState
->EnableCategories(enable
) ) 
2582         if ( m_windowStyle 
& wxPG_AUTO_SORT 
) 
2584             m_pState
->m_itemsAdded 
= 1; // force 
2585             PrepareAfterItemsAdded(); 
2589         m_pState
->m_itemsAdded 
= 1; 
2591     // No need for RecalculateVirtualSize() here - it is already called in 
2592     // wxPropertyGridPageState method above. 
2599 // ----------------------------------------------------------------------- 
2601 void wxPropertyGrid::SwitchState( wxPropertyGridPageState
* pNewState 
) 
2603     wxASSERT( pNewState 
); 
2604     wxASSERT( pNewState
->GetGrid() ); 
2606     if ( pNewState 
== m_pState 
) 
2609     wxArrayPGProperty oldSelection 
= m_pState
->m_selection
; 
2611     // Call ClearSelection() instead of DoClearSelection() 
2612     // so that selection clear events are not sent. 
2615     m_pState
->m_selection 
= oldSelection
; 
2617     bool orig_mode 
= m_pState
->IsInNonCatMode(); 
2618     bool new_state_mode 
= pNewState
->IsInNonCatMode(); 
2620     m_pState 
= pNewState
; 
2623     int pgWidth 
= GetClientSize().x
; 
2624     if ( HasVirtualWidth() ) 
2626         int minWidth 
= pgWidth
; 
2627         if ( pNewState
->m_width 
< minWidth 
) 
2629             pNewState
->m_width 
= minWidth
; 
2630             pNewState
->CheckColumnWidths(); 
2636         // Just in case, fully re-center splitter 
2637         //if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) ) 
2638         //    pNewState->m_fSplitterX = -1.0; 
2640         pNewState
->OnClientWidthChange(pgWidth
, 
2641                                        pgWidth 
- pNewState
->m_width
); 
2646     // If necessary, convert state to correct mode. 
2647     if ( orig_mode 
!= new_state_mode 
) 
2649         // This should refresh as well. 
2650         EnableCategories( orig_mode
?false:true ); 
2652     else if ( !m_frozen 
) 
2654         // Refresh, if not frozen. 
2655         m_pState
->PrepareAfterItemsAdded(); 
2657         // Reselect (Use SetSelection() instead of Do-variant so that 
2658         // events won't be sent). 
2659         SetSelection(m_pState
->m_selection
); 
2661         RecalculateVirtualSize(0); 
2665         m_pState
->m_itemsAdded 
= 1; 
2668 // ----------------------------------------------------------------------- 
2670 // Call to SetSplitterPosition will always disable splitter auto-centering 
2671 // if parent window is shown. 
2672 void wxPropertyGrid::DoSetSplitterPosition_( int newxpos
, bool refresh
, 
2676     if ( ( newxpos 
< wxPG_DRAG_MARGIN 
) ) 
2679     wxPropertyGridPageState
* state 
= m_pState
; 
2681     state
->DoSetSplitterPosition( newxpos
, splitterIndex
, allPages 
); 
2685         if ( GetSelection() ) 
2686             CorrectEditorWidgetSizeX(); 
2692 // ----------------------------------------------------------------------- 
2694 void wxPropertyGrid::CenterSplitter( bool enableAutoCentering 
) 
2696     SetSplitterPosition( m_width
/2 ); 
2697     if ( enableAutoCentering 
&& HasFlag(wxPG_SPLITTER_AUTO_CENTER
) ) 
2698         m_pState
->m_dontCenterSplitter 
= false; 
2701 // ----------------------------------------------------------------------- 
2702 // wxPropertyGrid item iteration (GetNextProperty etc.) methods 
2703 // ----------------------------------------------------------------------- 
2705 // Returns nearest paint visible property (such that will be painted unless 
2706 // window is scrolled or resized). If given property is paint visible, then 
2707 // it itself will be returned 
2708 wxPGProperty
* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty
* p 
) const 
2710     int vx
,vy1
;// Top left corner of client 
2711     GetViewStart(&vx
,&vy1
); 
2712     vy1 
*= wxPG_PIXELS_PER_UNIT
; 
2714     int vy2 
= vy1 
+ m_height
; 
2715     int propY 
= p
->GetY2(m_lineHeight
); 
2717     if ( (propY 
+ m_lineHeight
) < vy1 
) 
2720         return DoGetItemAtY( vy1 
); 
2722     else if ( propY 
> vy2 
) 
2725         return DoGetItemAtY( vy2 
); 
2728     // Itself paint visible 
2733 // ----------------------------------------------------------------------- 
2734 // Methods related to change in value, value modification and sending events 
2735 // ----------------------------------------------------------------------- 
2737 // commits any changes in editor of selected property 
2738 // return true if validation did not fail 
2739 // flags are same as with DoSelectProperty 
2740 bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags 
) 
2742     // Committing already? 
2743     if ( m_inCommitChangesFromEditor 
) 
2746     // Don't do this if already processing editor event. It might 
2747     // induce recursive dialogs and crap like that. 
2748     if ( m_iFlags 
& wxPG_FL_IN_HANDLECUSTOMEDITOREVENT 
) 
2750         if ( m_inDoPropertyChanged 
) 
2756     wxPGProperty
* selected 
= GetSelection(); 
2759          IsEditorsValueModified() && 
2760          (m_iFlags 
& wxPG_FL_INITIALIZED
) && 
2763         m_inCommitChangesFromEditor 
= 1; 
2765         wxVariant 
variant(selected
->GetValueRef()); 
2766         bool valueIsPending 
= false; 
2768         // JACS - necessary to avoid new focus being found spuriously within OnIdle 
2769         // due to another window getting focus 
2770         wxWindow
* oldFocus 
= m_curFocused
; 
2772         bool validationFailure 
= false; 
2773         bool forceSuccess 
= (flags 
& (wxPG_SEL_NOVALIDATE
|wxPG_SEL_FORCE
)) ? true : false; 
2775         m_chgInfo_changedProperty 
= NULL
; 
2777         // If truly modified, schedule value as pending. 
2778         if ( selected
->GetEditorClass()-> 
2779                 GetValueFromControl( variant
, 
2781                                      GetEditorControl() ) ) 
2783             if ( DoEditorValidate() && 
2784                  PerformValidation(selected
, variant
) ) 
2786                 valueIsPending 
= true; 
2790                 validationFailure 
= true; 
2795             EditorsValueWasNotModified(); 
2800         m_inCommitChangesFromEditor 
= 0; 
2802         if ( validationFailure 
&& !forceSuccess 
) 
2806                 oldFocus
->SetFocus(); 
2807                 m_curFocused 
= oldFocus
; 
2810             res 
= OnValidationFailure(selected
, variant
); 
2812             // Now prevent further validation failure messages 
2815                 EditorsValueWasNotModified(); 
2816                 OnValidationFailureReset(selected
); 
2819         else if ( valueIsPending 
) 
2821             DoPropertyChanged( selected
, flags 
); 
2822             EditorsValueWasNotModified(); 
2831 // ----------------------------------------------------------------------- 
2833 bool wxPropertyGrid::PerformValidation( wxPGProperty
* p
, wxVariant
& pendingValue
, 
2837     // Runs all validation functionality. 
2838     // Returns true if value passes all tests. 
2841     m_validationInfo
.m_failureBehavior 
= m_permanentValidationFailureBehavior
; 
2844     // Variant list a special value that cannot be validated 
2846     if ( pendingValue
.GetType() != wxPG_VARIANT_TYPE_LIST 
) 
2848         if ( !p
->ValidateValue(pendingValue
, m_validationInfo
) ) 
2853     // Adapt list to child values, if necessary 
2854     wxVariant listValue 
= pendingValue
; 
2855     wxVariant
* pPendingValue 
= &pendingValue
; 
2856     wxVariant
* pList 
= NULL
; 
2858     // If parent has wxPG_PROP_AGGREGATE flag, or uses composite 
2859     // string value, then we need treat as it was changed instead 
2860     // (or, in addition, as is the case with composite string parent). 
2861     // This includes creating list variant for child values. 
2863     wxPGProperty
* pwc 
= p
->GetParent(); 
2864     wxPGProperty
* changedProperty 
= p
; 
2865     wxPGProperty
* baseChangedProperty 
= changedProperty
; 
2866     wxVariant bcpPendingList
; 
2868     listValue 
= pendingValue
; 
2869     listValue
.SetName(p
->GetBaseName()); 
2872             (pwc
->HasFlag(wxPG_PROP_AGGREGATE
) || pwc
->HasFlag(wxPG_PROP_COMPOSED_VALUE
)) ) 
2874         wxVariantList tempList
; 
2875         wxVariant 
lv(tempList
, pwc
->GetBaseName()); 
2876         lv
.Append(listValue
); 
2878         pPendingValue 
= &listValue
; 
2880         if ( pwc
->HasFlag(wxPG_PROP_AGGREGATE
) ) 
2882             baseChangedProperty 
= pwc
; 
2883             bcpPendingList 
= lv
; 
2886         changedProperty 
= pwc
; 
2887         pwc 
= pwc
->GetParent(); 
2891     wxPGProperty
* evtChangingProperty 
= changedProperty
; 
2893     if ( pPendingValue
->GetType() != wxPG_VARIANT_TYPE_LIST 
) 
2895         value 
= *pPendingValue
; 
2899         // Convert list to child values 
2900         pList 
= pPendingValue
; 
2901         changedProperty
->AdaptListToValue( *pPendingValue
, &value 
); 
2904     wxVariant evtChangingValue 
= value
; 
2906     if ( flags 
& SendEvtChanging 
) 
2908         // FIXME: After proper ValueToString()s added, remove 
2909         // this. It is just a temporary fix, as evt_changing 
2910         // will simply not work for wxPG_PROP_COMPOSED_VALUE 
2911         // (unless it is selected, and textctrl editor is open). 
2912         if ( changedProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
2914             evtChangingProperty 
= baseChangedProperty
; 
2915             if ( evtChangingProperty 
!= p 
) 
2917                 evtChangingProperty
->AdaptListToValue( bcpPendingList
, &evtChangingValue 
); 
2921                 evtChangingValue 
= pendingValue
; 
2925         if ( evtChangingProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
2927             if ( changedProperty 
== GetSelection() ) 
2929                 wxWindow
* editor 
= GetEditorControl(); 
2930                 wxASSERT( editor
->IsKindOf(CLASSINFO(wxTextCtrl
)) ); 
2931                 evtChangingValue 
= wxStaticCast(editor
, wxTextCtrl
)->GetValue(); 
2935                 wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value.")); 
2940     wxASSERT( m_chgInfo_changedProperty 
== NULL 
); 
2941     m_chgInfo_changedProperty 
= changedProperty
; 
2942     m_chgInfo_baseChangedProperty 
= baseChangedProperty
; 
2943     m_chgInfo_pendingValue 
= value
; 
2946         m_chgInfo_valueList 
= *pList
; 
2948         m_chgInfo_valueList
.MakeNull(); 
2950     // If changedProperty is not property which value was edited, 
2951     // then call wxPGProperty::ValidateValue() for that as well. 
2952     if ( p 
!= changedProperty 
&& value
.GetType() != wxPG_VARIANT_TYPE_LIST 
) 
2954         if ( !changedProperty
->ValidateValue(value
, m_validationInfo
) ) 
2958     if ( flags 
& SendEvtChanging 
) 
2960         // SendEvent returns true if event was vetoed 
2961         if ( SendEvent( wxEVT_PG_CHANGING
, evtChangingProperty
, 
2962                         &evtChangingValue 
) ) 
2966     if ( flags 
& IsStandaloneValidation 
) 
2968         // If called in 'generic' context, we need to reset 
2969         // m_chgInfo_changedProperty and write back translated value. 
2970         m_chgInfo_changedProperty 
= NULL
; 
2971         pendingValue 
= value
; 
2977 // ----------------------------------------------------------------------- 
2979 void wxPropertyGrid::DoShowPropertyError( wxPGProperty
* WXUNUSED(property
), const wxString
& msg 
) 
2981     if ( !msg
.length() ) 
2985     if ( !wxPGGlobalVars
->m_offline 
) 
2987         wxWindow
* topWnd 
= ::wxGetTopLevelParent(this); 
2990             wxFrame
* pFrame 
= wxDynamicCast(topWnd
, wxFrame
); 
2993                 wxStatusBar
* pStatusBar 
= pFrame
->GetStatusBar(); 
2996                     pStatusBar
->SetStatusText(msg
); 
3004     ::wxMessageBox(msg
, wxT("Property Error")); 
3007 // ----------------------------------------------------------------------- 
3009 bool wxPropertyGrid::OnValidationFailure( wxPGProperty
* property
, 
3010                                           wxVariant
& invalidValue 
) 
3012     wxWindow
* editor 
= GetEditorControl(); 
3014     // First call property's handler 
3015     property
->OnValidationFailure(invalidValue
); 
3017     bool res 
= DoOnValidationFailure(property
, invalidValue
); 
3020     // For non-wxTextCtrl editors, we do need to revert the value 
3021     if ( !editor
->IsKindOf(CLASSINFO(wxTextCtrl
)) && 
3022          property 
== GetSelection() ) 
3024         property
->GetEditorClass()->UpdateControl(property
, editor
); 
3027     property
->SetFlag(wxPG_PROP_INVALID_VALUE
); 
3032 bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty
* property
, wxVariant
& WXUNUSED(invalidValue
) ) 
3034     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3036     if ( vfb 
& wxPG_VFB_BEEP 
) 
3039     if ( (vfb 
& wxPG_VFB_MARK_CELL
) && 
3040          !property
->HasFlag(wxPG_PROP_INVALID_VALUE
) ) 
3042         unsigned int colCount 
= m_pState
->GetColumnCount(); 
3044         // We need backup marked property's cells 
3045         m_propCellsBackup 
= property
->m_cells
; 
3047         wxColour vfbFg 
= *wxWHITE
; 
3048         wxColour vfbBg 
= *wxRED
; 
3050         property
->EnsureCells(colCount
); 
3052         for ( unsigned int i
=0; i
<colCount
; i
++ ) 
3054             wxPGCell
& cell 
= property
->m_cells
[i
]; 
3055             cell
.SetFgCol(vfbFg
); 
3056             cell
.SetBgCol(vfbBg
); 
3059         DrawItemAndChildren(property
); 
3061         if ( property 
== GetSelection() ) 
3063             SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
); 
3065             wxWindow
* editor 
= GetEditorControl(); 
3068                 editor
->SetForegroundColour(vfbFg
); 
3069                 editor
->SetBackgroundColour(vfbBg
); 
3074     if ( vfb 
& wxPG_VFB_SHOW_MESSAGE 
) 
3076         wxString msg 
= m_validationInfo
.m_failureMessage
; 
3078         if ( !msg
.length() ) 
3079             msg 
= wxT("You have entered invalid value. Press ESC to cancel editing."); 
3081         DoShowPropertyError(property
, msg
); 
3084     return (vfb 
& wxPG_VFB_STAY_IN_PROPERTY
) ? false : true; 
3087 // ----------------------------------------------------------------------- 
3089 void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty
* property 
) 
3091     int vfb 
= m_validationInfo
.m_failureBehavior
; 
3093     if ( vfb 
& wxPG_VFB_MARK_CELL 
) 
3096         property
->m_cells 
= m_propCellsBackup
; 
3098         ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL
); 
3100         if ( property 
== GetSelection() && GetEditorControl() ) 
3102             // Calling this will recreate the control, thus resetting its colour 
3103             RefreshProperty(property
); 
3107             DrawItemAndChildren(property
); 
3112 // ----------------------------------------------------------------------- 
3114 // flags are same as with DoSelectProperty 
3115 bool wxPropertyGrid::DoPropertyChanged( wxPGProperty
* p
, unsigned int selFlags 
) 
3117     if ( m_inDoPropertyChanged 
) 
3120     wxWindow
* editor 
= GetEditorControl(); 
3121     wxPGProperty
* selected 
= GetSelection(); 
3123     m_pState
->m_anyModified 
= 1; 
3125     m_inDoPropertyChanged 
= 1; 
3127     // Maybe need to update control 
3128     wxASSERT( m_chgInfo_changedProperty 
!= NULL 
); 
3130     // These values were calculated in PerformValidation() 
3131     wxPGProperty
* changedProperty 
= m_chgInfo_changedProperty
; 
3132     wxVariant value 
= m_chgInfo_pendingValue
; 
3134     wxPGProperty
* topPaintedProperty 
= changedProperty
; 
3136     while ( !topPaintedProperty
->IsCategory() && 
3137             !topPaintedProperty
->IsRoot() ) 
3139         topPaintedProperty 
= topPaintedProperty
->GetParent(); 
3142     changedProperty
->SetValue(value
, &m_chgInfo_valueList
, wxPG_SETVAL_BY_USER
); 
3144     // Set as Modified (not if dragging just began) 
3145     if ( !(p
->m_flags 
& wxPG_PROP_MODIFIED
) ) 
3147         p
->m_flags 
|= wxPG_PROP_MODIFIED
; 
3148         if ( p 
== selected 
&& (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3151                 SetCurControlBoldFont(); 
3157     // Propagate updates to parent(s) 
3159     wxPGProperty
* prevPwc 
= NULL
; 
3161     while ( prevPwc 
!= topPaintedProperty 
) 
3163         pwc
->m_flags 
|= wxPG_PROP_MODIFIED
; 
3165         if ( pwc 
== selected 
&& (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3168                 SetCurControlBoldFont(); 
3172         pwc 
= pwc
->GetParent(); 
3175     // Draw the actual property 
3176     DrawItemAndChildren( topPaintedProperty 
); 
3179     // If value was set by wxPGProperty::OnEvent, then update the editor 
3181     if ( selFlags 
& wxPG_SEL_DIALOGVAL 
) 
3187 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
3188         if ( m_wndEditor 
) m_wndEditor
->Refresh(); 
3189         if ( m_wndEditor2 
) m_wndEditor2
->Refresh(); 
3194     wxASSERT( !changedProperty
->GetParent()->HasFlag(wxPG_PROP_AGGREGATE
) ); 
3196     // If top parent has composite string value, then send to child parents, 
3197     // starting from baseChangedProperty. 
3198     if ( changedProperty
->HasFlag(wxPG_PROP_COMPOSED_VALUE
) ) 
3200         pwc 
= m_chgInfo_baseChangedProperty
; 
3202         while ( pwc 
!= changedProperty 
) 
3204             SendEvent( wxEVT_PG_CHANGED
, pwc
, NULL 
); 
3205             pwc 
= pwc
->GetParent(); 
3209     SendEvent( wxEVT_PG_CHANGED
, changedProperty
, NULL 
); 
3211     m_inDoPropertyChanged 
= 0; 
3216 // ----------------------------------------------------------------------- 
3218 bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id
, wxVariant newValue 
) 
3220     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
3222     m_chgInfo_changedProperty 
= NULL
; 
3224     if ( PerformValidation(p
, newValue
) ) 
3226         DoPropertyChanged(p
); 
3231         OnValidationFailure(p
, newValue
); 
3237 // ----------------------------------------------------------------------- 
3239 wxVariant 
wxPropertyGrid::GetUncommittedPropertyValue() 
3241     wxPGProperty
* prop 
= GetSelectedProperty(); 
3244         return wxNullVariant
; 
3246     wxTextCtrl
* tc 
= GetEditorTextCtrl(); 
3247     wxVariant value 
= prop
->GetValue(); 
3249     if ( !tc 
|| !IsEditorsValueModified() ) 
3252     if ( !prop
->StringToValue(value
, tc
->GetValue()) ) 
3255     if ( !PerformValidation(prop
, value
, IsStandaloneValidation
) ) 
3256         return prop
->GetValue(); 
3261 // ----------------------------------------------------------------------- 
3263 // Runs wxValidator for the selected property 
3264 bool wxPropertyGrid::DoEditorValidate() 
3269 // ----------------------------------------------------------------------- 
3271 void wxPropertyGrid::HandleCustomEditorEvent( wxEvent 
&event 
) 
3273     wxPGProperty
* selected 
= GetSelection(); 
3275     // Somehow, event is handled after property has been deselected. 
3276     // Possibly, but very rare. 
3278           selected
->HasFlag(wxPG_PROP_BEING_DELETED
) || 
3279           // Also don't handle editor event if wxEVT_PG_CHANGED or 
3280           // similar is currently doing something (showing a 
3281           // message box, for instance). 
3285     if ( m_iFlags 
& wxPG_FL_IN_HANDLECUSTOMEDITOREVENT 
) 
3288     wxVariant 
pendingValue(selected
->GetValueRef()); 
3289     wxWindow
* wnd 
= GetEditorControl(); 
3290     wxWindow
* editorWnd 
= wxDynamicCast(event
.GetEventObject(), wxWindow
); 
3292     bool wasUnspecified 
= selected
->IsValueUnspecified(); 
3293     int usesAutoUnspecified 
= selected
->UsesAutoUnspecified(); 
3294     bool valueIsPending 
= false; 
3296     m_chgInfo_changedProperty 
= NULL
; 
3298     m_iFlags 
&= ~(wxPG_FL_VALIDATION_FAILED
|wxPG_FL_VALUE_CHANGE_IN_EVENT
); 
3301     // Filter out excess wxTextCtrl modified events 
3302     if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED 
&& 
3304          wnd
->IsKindOf(CLASSINFO(wxTextCtrl
)) ) 
3306         wxTextCtrl
* tc 
= (wxTextCtrl
*) wnd
; 
3308         wxString newTcValue 
= tc
->GetValue(); 
3309         if ( m_prevTcValue 
== newTcValue 
) 
3312         m_prevTcValue 
= newTcValue
; 
3315     SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
); 
3317     bool validationFailure 
= false; 
3318     bool buttonWasHandled 
= false; 
3321     // Try common button handling 
3322     if ( m_wndEditor2 
&& event
.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED 
) 
3324         wxPGEditorDialogAdapter
* adapter 
= selected
->GetEditorDialog(); 
3328             buttonWasHandled 
= true; 
3329             // Store as res2, as previously (and still currently alternatively) 
3330             // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED 
3331             // in wxPGProperty::OnEvent(). 
3332             adapter
->ShowDialog( this, selected 
); 
3337     if ( !buttonWasHandled 
) 
3339         if ( wnd 
|| m_wndEditor2 
) 
3341             // First call editor class' event handler. 
3342             const wxPGEditor
* editor 
= selected
->GetEditorClass(); 
3344             if ( editor
->OnEvent( this, selected
, editorWnd
, event 
) ) 
3346                 // If changes, validate them 
3347                 if ( DoEditorValidate() ) 
3349                     if ( editor
->GetValueFromControl( pendingValue
, 
3352                         valueIsPending 
= true; 
3356                     validationFailure 
= true; 
3361         // Then the property's custom handler (must be always called, unless 
3362         // validation failed). 
3363         if ( !validationFailure 
) 
3364             buttonWasHandled 
= selected
->OnEvent( this, editorWnd
, event 
); 
3367     // SetValueInEvent(), as called in one of the functions referred above 
3368     // overrides editor's value. 
3369     if ( m_iFlags 
& wxPG_FL_VALUE_CHANGE_IN_EVENT 
) 
3371         valueIsPending 
= true; 
3372         pendingValue 
= m_changeInEventValue
; 
3373         selFlags 
|= wxPG_SEL_DIALOGVAL
; 
3376     if ( !validationFailure 
&& valueIsPending 
) 
3377         if ( !PerformValidation(selected
, pendingValue
) ) 
3378             validationFailure 
= true; 
3380     if ( validationFailure
) 
3382         OnValidationFailure(selected
, pendingValue
); 
3384     else if ( valueIsPending 
) 
3386         selFlags 
|= ( !wasUnspecified 
&& selected
->IsValueUnspecified() && usesAutoUnspecified 
) ? wxPG_SEL_SETUNSPEC 
: 0; 
3388         DoPropertyChanged(selected
, selFlags
); 
3389         EditorsValueWasNotModified(); 
3391         // Regardless of editor type, unfocus editor on 
3392         // text-editing related enter press. 
3393         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3400         // No value after all 
3402         // Regardless of editor type, unfocus editor on 
3403         // text-editing related enter press. 
3404         if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER 
) 
3409         // Let unhandled button click events go to the parent 
3410         if ( !buttonWasHandled 
&& event
.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED 
) 
3412             wxCommandEvent 
evt(wxEVT_COMMAND_BUTTON_CLICKED
,GetId()); 
3413             GetEventHandler()->AddPendingEvent(evt
); 
3417     ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT
); 
3420 // ----------------------------------------------------------------------- 
3421 // wxPropertyGrid editor control helper methods 
3422 // ----------------------------------------------------------------------- 
3424 wxRect 
wxPropertyGrid::GetEditorWidgetRect( wxPGProperty
* p
, int column 
) const 
3426     int itemy 
= p
->GetY2(m_lineHeight
); 
3427     int splitterX 
= m_pState
->DoGetSplitterPosition(column
-1); 
3428     int colEnd 
= splitterX 
+ m_pState
->m_colWidths
[column
]; 
3429     int imageOffset 
= 0; 
3431     int vx
, vy
;  // Top left corner of client 
3432     GetViewStart(&vx
, &vy
); 
3433     vy 
*= wxPG_PIXELS_PER_UNIT
; 
3435     // TODO: If custom image detection changes from current, change this. 
3436     if ( m_iFlags 
& wxPG_FL_CUR_USES_CUSTOM_IMAGE 
) 
3438         //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; 
3439         int iw 
= p
->OnMeasureImage().x
; 
3441             iw 
= wxPG_CUSTOM_IMAGE_WIDTH
; 
3442         imageOffset 
= p
->GetImageOffset(iw
); 
3447         splitterX
+imageOffset
+wxPG_XBEFOREWIDGET
+wxPG_CONTROL_MARGIN
+1, 
3449         colEnd
-splitterX
-wxPG_XBEFOREWIDGET
-wxPG_CONTROL_MARGIN
-imageOffset
-1, 
3454 // ----------------------------------------------------------------------- 
3456 wxRect 
wxPropertyGrid::GetImageRect( wxPGProperty
* p
, int item 
) const 
3458     wxSize sz 
= GetImageSize(p
, item
); 
3459     return wxRect(wxPG_CONTROL_MARGIN 
+ wxCC_CUSTOM_IMAGE_MARGIN1
, 
3460                   wxPG_CUSTOM_IMAGE_SPACINGY
, 
3465 // return size of custom paint image 
3466 wxSize 
wxPropertyGrid::GetImageSize( wxPGProperty
* p
, int item 
) const 
3468     // If called with NULL property, then return default image 
3469     // size for properties that use image. 
3471         return wxSize(wxPG_CUSTOM_IMAGE_WIDTH
,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
)); 
3473     wxSize cis 
= p
->OnMeasureImage(item
); 
3475     int choiceCount 
= p
->m_choices
.GetCount(); 
3476     int comVals 
= p
->GetDisplayedCommonValueCount(); 
3477     if ( item 
>= choiceCount 
&& comVals 
> 0 ) 
3479         unsigned int cvi 
= item
-choiceCount
; 
3480         cis 
= GetCommonValue(cvi
)->GetRenderer()->GetImageSize(NULL
, 1, cvi
); 
3482     else if ( item 
>= 0 && choiceCount 
== 0 ) 
3483         return wxSize(0, 0); 
3488             cis
.x 
= wxPG_CUSTOM_IMAGE_WIDTH
; 
3493             cis
.y 
= wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
); 
3500 // ----------------------------------------------------------------------- 
3502 // takes scrolling into account 
3503 void wxPropertyGrid::ImprovedClientToScreen( int* px
, int* py 
) 
3506     GetViewStart(&vx
,&vy
); 
3507     vy
*=wxPG_PIXELS_PER_UNIT
; 
3508     vx
*=wxPG_PIXELS_PER_UNIT
; 
3511     ClientToScreen( px
, py 
); 
3514 // ----------------------------------------------------------------------- 
3516 wxPropertyGridHitTestResult 
wxPropertyGrid::HitTest( const wxPoint
& pt 
) const 
3519     GetViewStart(&pt2
.x
,&pt2
.y
); 
3520     pt2
.x 
*= wxPG_PIXELS_PER_UNIT
; 
3521     pt2
.y 
*= wxPG_PIXELS_PER_UNIT
; 
3525     return m_pState
->HitTest(pt2
); 
3528 // ----------------------------------------------------------------------- 
3530 // custom set cursor 
3531 void wxPropertyGrid::CustomSetCursor( int type
, bool override 
) 
3533     if ( type 
== m_curcursor 
&& !override 
) return; 
3535     wxCursor
* cursor 
= &wxPG_DEFAULT_CURSOR
; 
3537     if ( type 
== wxCURSOR_SIZEWE 
) 
3538         cursor 
= m_cursorSizeWE
; 
3540     SetCursor( *cursor 
); 
3545 // ----------------------------------------------------------------------- 
3546 // wxPropertyGrid property selection, editor creation 
3547 // ----------------------------------------------------------------------- 
3550 // This class forwards events from property editor controls to wxPropertyGrid. 
3551 class wxPropertyGridEditorEventForwarder 
: public wxEvtHandler
 
3554     wxPropertyGridEditorEventForwarder( wxPropertyGrid
* propGrid 
) 
3555         : wxEvtHandler(), m_propGrid(propGrid
) 
3559     virtual ~wxPropertyGridEditorEventForwarder() 
3564     bool ProcessEvent( wxEvent
& event 
) 
3569         m_propGrid
->HandleCustomEditorEvent(event
); 
3571         return wxEvtHandler::ProcessEvent(event
); 
3574     wxPropertyGrid
*         m_propGrid
; 
3577 // Setups event handling for child control 
3578 void wxPropertyGrid::SetupChildEventHandling( wxWindow
* argWnd 
) 
3580     wxWindowID id 
= argWnd
->GetId(); 
3582     if ( argWnd 
== m_wndEditor 
) 
3584         argWnd
->Connect(id
, wxEVT_MOTION
, 
3585             wxMouseEventHandler(wxPropertyGrid::OnMouseMoveChild
), 
3587         argWnd
->Connect(id
, wxEVT_LEFT_UP
, 
3588             wxMouseEventHandler(wxPropertyGrid::OnMouseUpChild
), 
3590         argWnd
->Connect(id
, wxEVT_LEFT_DOWN
, 
3591             wxMouseEventHandler(wxPropertyGrid::OnMouseClickChild
), 
3593         argWnd
->Connect(id
, wxEVT_RIGHT_UP
, 
3594             wxMouseEventHandler(wxPropertyGrid::OnMouseRightClickChild
), 
3596         argWnd
->Connect(id
, wxEVT_ENTER_WINDOW
, 
3597             wxMouseEventHandler(wxPropertyGrid::OnMouseEntry
), 
3599         argWnd
->Connect(id
, wxEVT_LEAVE_WINDOW
, 
3600             wxMouseEventHandler(wxPropertyGrid::OnMouseEntry
), 
3604     wxPropertyGridEditorEventForwarder
* forwarder
; 
3605     forwarder 
= new wxPropertyGridEditorEventForwarder(this); 
3606     argWnd
->PushEventHandler(forwarder
); 
3608     argWnd
->Connect(id
, wxEVT_KEY_DOWN
, 
3609         wxCharEventHandler(wxPropertyGrid::OnChildKeyDown
), 
3613 void wxPropertyGrid::DestroyEditorWnd( wxWindow
* wnd 
) 
3620     // Do not free editors immediately (for sake of processing events) 
3621     wxPendingDelete
.Append(wnd
); 
3624 void wxPropertyGrid::FreeEditors() 
3627     // Return focus back to canvas from children (this is required at least for 
3628     // GTK+, which, unlike Windows, clears focus when control is destroyed 
3629     // instead of moving it to closest parent). 
3630     wxWindow
* focus 
= wxWindow::FindFocus(); 
3633         wxWindow
* parent 
= focus
->GetParent(); 
3636             if ( parent 
== this ) 
3641             parent 
= parent
->GetParent(); 
3645     // Do not free editors immediately if processing events 
3648         wxEvtHandler
* handler 
= m_wndEditor2
->PopEventHandler(false); 
3649         m_wndEditor2
->Hide(); 
3650         wxPendingDelete
.Append( handler 
); 
3651         DestroyEditorWnd(m_wndEditor2
); 
3652         m_wndEditor2 
= NULL
; 
3657         wxEvtHandler
* handler 
= m_wndEditor
->PopEventHandler(false); 
3658         m_wndEditor
->Hide(); 
3659         wxPendingDelete
.Append( handler 
); 
3660         DestroyEditorWnd(m_wndEditor
); 
3665 // Call with NULL to de-select property 
3666 bool wxPropertyGrid::DoSelectProperty( wxPGProperty
* p
, unsigned int flags 
) 
3671         wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(), 
3672             p->m_parent->m_label.c_str(),p->GetIndexInParent()); 
3676         wxLogDebug(wxT("SelectProperty( NULL, -1 )")); 
3680     if ( m_inDoSelectProperty 
) 
3683     m_inDoSelectProperty 
= 1; 
3687         m_inDoSelectProperty 
= 0; 
3691     wxArrayPGProperty prevSelection 
= m_pState
->m_selection
; 
3692     wxPGProperty
* prevFirstSel
; 
3694     if ( prevSelection
.size() > 0 ) 
3695         prevFirstSel 
= prevSelection
[0]; 
3697         prevFirstSel 
= NULL
; 
3699     if ( prevFirstSel 
&& prevFirstSel
->HasFlag(wxPG_PROP_BEING_DELETED
) ) 
3700         prevFirstSel 
= NULL
; 
3702     // Always send event, as this is indirect call 
3703     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
); 
3707         wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() ); 
3709         wxPrintf( "None selected\n" ); 
3712         wxPrintf( "P =  %s\n", p->GetClassInfo()->GetClassName() ); 
3714         wxPrintf( "P = NULL\n" ); 
3717     // If we are frozen, then just set the values. 
3720         m_iFlags 
&= ~(wxPG_FL_ABNORMAL_EDITOR
); 
3721         m_editorFocused 
= 0; 
3722         m_pState
->DoSetSelection(p
); 
3724         // If frozen, always free controls. But don't worry, as Thaw will 
3725         // recall SelectProperty to recreate them. 
3728         // Prevent any further selection measures in this call 
3734         if ( prevFirstSel 
== p 
&& 
3735              prevSelection
.size() <= 1 && 
3736              !(flags 
& wxPG_SEL_FORCE
) ) 
3738             // Only set focus if not deselecting 
3741                 if ( flags 
& wxPG_SEL_FOCUS 
) 
3745                         m_wndEditor
->SetFocus(); 
3746                         m_editorFocused 
= 1; 
3755             m_inDoSelectProperty 
= 0; 
3760         // First, deactivate previous 
3763             OnValidationFailureReset(prevFirstSel
); 
3765             // Must double-check if this is an selected in case of forceswitch 
3766             if ( p 
!= prevFirstSel 
) 
3768                 if ( !CommitChangesFromEditor(flags
) ) 
3770                     // Validation has failed, so we can't exit the previous editor 
3771                     //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."), 
3772                     //               _("Invalid Value"),wxOK|wxICON_ERROR); 
3773                     m_inDoSelectProperty 
= 0; 
3780             m_iFlags 
&= ~(wxPG_FL_ABNORMAL_EDITOR
); 
3781             EditorsValueWasNotModified(); 
3784         SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY
); 
3786         m_pState
->DoSetSelection(p
); 
3788         // Redraw unselected 
3789         for ( unsigned int i
=0; i
<prevSelection
.size(); i
++ ) 
3791             DrawItem(prevSelection
[i
]); 
3795         // Then, activate the one given. 
3798             int propY 
= p
->GetY2(m_lineHeight
); 
3800             int splitterX 
= GetSplitterPosition(); 
3801             m_editorFocused 
= 0; 
3802             m_iFlags 
|= wxPG_FL_PRIMARY_FILLS_ENTIRE
; 
3803             if ( p 
!= prevFirstSel 
) 
3804                 m_iFlags 
&= ~(wxPG_FL_VALIDATION_FAILED
); 
3806             wxASSERT( m_wndEditor 
== NULL 
); 
3809             // Only create editor for non-disabled non-caption 
3810             if ( !p
->IsCategory() && !(p
->m_flags 
& wxPG_PROP_DISABLED
) ) 
3812             // do this for non-caption items 
3816                 // Do we need to paint the custom image, if any? 
3817                 m_iFlags 
&= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE
); 
3818                 if ( (p
->m_flags 
& wxPG_PROP_CUSTOMIMAGE
) && 
3819                      !p
->GetEditorClass()->CanContainCustomImage() 
3821                     m_iFlags 
|= wxPG_FL_CUR_USES_CUSTOM_IMAGE
; 
3823                 wxRect grect 
= GetEditorWidgetRect(p
, m_selColumn
); 
3824                 wxPoint goodPos 
= grect
.GetPosition(); 
3826                 const wxPGEditor
* editor 
= p
->GetEditorClass(); 
3827                 wxCHECK_MSG(editor
, false, 
3828                     wxT("NULL editor class not allowed")); 
3830                 m_iFlags 
&= ~wxPG_FL_FIXED_WIDTH_EDITOR
; 
3832                 wxPGWindowList wndList 
= 
3833                     editor
->CreateControls(this, 
3838                 m_wndEditor 
= wndList
.m_primary
; 
3839                 m_wndEditor2 
= wndList
.m_secondary
; 
3840                 wxWindow
* primaryCtrl 
= GetEditorControl(); 
3843                 // Essentially, primaryCtrl == m_wndEditor 
3846                 // NOTE: It is allowed for m_wndEditor to be NULL - in this 
3847                 //       case value is drawn as normal, and m_wndEditor2 is 
3848                 //       assumed to be a right-aligned button that triggers 
3849                 //       a separate editorCtrl window. 
3853                     wxASSERT_MSG( m_wndEditor
->GetParent() == GetPanel(), 
3854                                   "CreateControls must use result of " 
3855                                   "wxPropertyGrid::GetPanel() as parent " 
3858                     // Set validator, if any 
3859                 #if wxUSE_VALIDATORS 
3860                     wxValidator
* validator 
= p
->GetValidator(); 
3862                         primaryCtrl
->SetValidator(*validator
); 
3865                     if ( m_wndEditor
->GetSize().y 
> (m_lineHeight
+6) ) 
3866                         m_iFlags 
|= wxPG_FL_ABNORMAL_EDITOR
; 
3868                     // If it has modified status, use bold font 
3869                     // (must be done before capturing m_ctrlXAdjust) 
3870                     if ( (p
->m_flags 
& wxPG_PROP_MODIFIED
) && 
3871                          (m_windowStyle 
& wxPG_BOLD_MODIFIED
) ) 
3872                         SetCurControlBoldFont(); 
3874                     // Store x relative to splitter (we'll need it). 
3875                     m_ctrlXAdjust 
= m_wndEditor
->GetPosition().x 
- splitterX
; 
3877                     // Check if background clear is not necessary 
3878                     wxPoint pos 
= m_wndEditor
->GetPosition(); 
3879                     if ( pos
.x 
> (splitterX
+1) || pos
.y 
> propY 
) 
3881                         m_iFlags 
&= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE
); 
3884                     m_wndEditor
->SetSizeHints(3, 3); 
3886                     SetupChildEventHandling(primaryCtrl
); 
3888                     // Focus and select all (wxTextCtrl, wxComboBox etc) 
3889                     if ( flags 
& wxPG_SEL_FOCUS 
) 
3891                         primaryCtrl
->SetFocus(); 
3893                         p
->GetEditorClass()->OnFocus(p
, primaryCtrl
); 
3899                     wxASSERT_MSG( m_wndEditor2
->GetParent() == GetPanel(), 
3900                                   "CreateControls must use result of " 
3901                                   "wxPropertyGrid::GetPanel() as parent " 
3904                     // Get proper id for wndSecondary 
3905                     m_wndSecId 
= m_wndEditor2
->GetId(); 
3906                     wxWindowList children 
= m_wndEditor2
->GetChildren(); 
3907                     wxWindowList::iterator node 
= children
.begin(); 
3908                     if ( node 
!= children
.end() ) 
3909                         m_wndSecId 
= ((wxWindow
*)*node
)->GetId(); 
3911                     m_wndEditor2
->SetSizeHints(3,3); 
3913                     m_wndEditor2
->Show(); 
3915                     SetupChildEventHandling(m_wndEditor2
); 
3917                     // If no primary editor, focus to button to allow 
3918                     // it to interprete ENTER etc. 
3919                     // NOTE: Due to problems focusing away from it, this 
3920                     //       has been disabled. 
3922                     if ( (flags & wxPG_SEL_FOCUS) && !m_wndEditor ) 
3923                         m_wndEditor2->SetFocus(); 
3927                 if ( flags 
& wxPG_SEL_FOCUS 
) 
3928                     m_editorFocused 
= 1; 
3933                 // Make sure focus is in grid canvas (important for wxGTK, 
3938             EditorsValueWasNotModified(); 
3940             // If it's inside collapsed section, expand parent, scroll, etc. 
3941             // Also, if it was partially visible, scroll it into view. 
3942             if ( !(flags 
& wxPG_SEL_NONVISIBLE
) ) 
3947                 m_wndEditor
->Show(true); 
3950             if ( !(flags 
& wxPG_SEL_NO_REFRESH
) ) 
3955             // Make sure focus is in grid canvas 
3959         ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY
); 
3965     // Show help text in status bar. 
3966     //   (if found and grid not embedded in manager with help box and 
3967     //    style wxPG_EX_HELP_AS_TOOLTIPS is not used). 
3970     if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS
) ) 
3972         wxStatusBar
* statusbar 
= NULL
; 
3973         if ( !(m_iFlags 
& wxPG_FL_NOSTATUSBARHELP
) ) 
3975             wxFrame
* frame 
= wxDynamicCast(::wxGetTopLevelParent(this),wxFrame
); 
3977                 statusbar 
= frame
->GetStatusBar(); 
3982             const wxString
* pHelpString 
= (const wxString
*) NULL
; 
3986                 pHelpString 
= &p
->GetHelpString(); 
3987                 if ( pHelpString
->length() ) 
3989                     // Set help box text. 
3990                     statusbar
->SetStatusText( *pHelpString 
); 
3991                     m_iFlags 
|= wxPG_FL_STRING_IN_STATUSBAR
; 
3995             if ( (!pHelpString 
|| !pHelpString
->length()) && 
3996                  (m_iFlags 
& wxPG_FL_STRING_IN_STATUSBAR
) ) 
3998                 // Clear help box - but only if it was written 
3999                 // by us at previous time. 
4000                 statusbar
->SetStatusText( m_emptyString 
); 
4001                 m_iFlags 
&= ~(wxPG_FL_STRING_IN_STATUSBAR
); 
4007     m_inDoSelectProperty 
= 0; 
4009     // call wx event handler (here so that it also occurs on deselection) 
4010     if ( !(flags 
& wxPG_SEL_DONT_SEND_EVENT
) ) 
4011         SendEvent( wxEVT_PG_SELECTED
, p
, NULL 
); 
4016 // ----------------------------------------------------------------------- 
4018 bool wxPropertyGrid::UnfocusEditor() 
4020     wxPGProperty
* selected 
= GetSelection(); 
4022     if ( !selected 
|| !m_wndEditor 
|| m_frozen 
) 
4025     if ( !CommitChangesFromEditor(0) ) 
4034 // ----------------------------------------------------------------------- 
4036 void wxPropertyGrid::RefreshEditor() 
4038     wxPGProperty
* p 
= GetSelection(); 
4042     wxWindow
* wnd 
= GetEditorControl(); 
4046     // Set editor font boldness - must do this before 
4047     // calling UpdateControl(). 
4048     if ( HasFlag(wxPG_BOLD_MODIFIED
) ) 
4050         if ( p
->HasFlag(wxPG_PROP_MODIFIED
) ) 
4051             wnd
->SetFont(GetCaptionFont()); 
4053             wnd
->SetFont(GetFont()); 
4056     const wxPGEditor
* editorClass 
= p
->GetEditorClass(); 
4058     editorClass
->UpdateControl(p
, wnd
); 
4060     if ( p
->IsValueUnspecified() ) 
4061         editorClass 
->SetValueToUnspecified(p
, wnd
); 
4064 // ----------------------------------------------------------------------- 
4066 bool wxPropertyGrid::SelectProperty( wxPGPropArg id
, bool focus 
) 
4068     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) 
4070     int flags 
= wxPG_SEL_DONT_SEND_EVENT
; 
4072         flags 
|= wxPG_SEL_FOCUS
; 
4074     return DoSelectProperty(p
, flags
); 
4077 // ----------------------------------------------------------------------- 
4078 // wxPropertyGrid expand/collapse state 
4079 // ----------------------------------------------------------------------- 
4081 bool wxPropertyGrid::DoCollapse( wxPGProperty
* p
, bool sendEvents 
) 
4083     wxPGProperty
* pwc 
= wxStaticCast(p
, wxPGProperty
); 
4084     wxPGProperty
* selected 
= GetSelection(); 
4086     // If active editor was inside collapsed section, then disable it 
4087     if ( selected 
&& selected
->IsSomeParent(p
) ) 
4092     // Store dont-center-splitter flag 'cause we need to temporarily set it 
4093     bool prevDontCenterSplitter 
= m_pState
->m_dontCenterSplitter
; 
4094     m_pState
->m_dontCenterSplitter 
= true; 
4096     bool res 
= m_pState
->DoCollapse(pwc
); 
4101             SendEvent( wxEVT_PG_ITEM_COLLAPSED
, p 
); 
4103         RecalculateVirtualSize(); 
4107     m_pState
->m_dontCenterSplitter 
= prevDontCenterSplitter
; 
4112 // ----------------------------------------------------------------------- 
4114 bool wxPropertyGrid::DoExpand( wxPGProperty
* p
, bool sendEvents 
) 
4116     wxCHECK_MSG( p
, false, wxT("invalid property id") ); 
4118     wxPGProperty
* pwc 
= (wxPGProperty
*)p
; 
4120     // Store dont-center-splitter flag 'cause we need to temporarily set it 
4121     bool prevDontCenterSplitter 
= m_pState
->m_dontCenterSplitter
; 
4122     m_pState
->m_dontCenterSplitter 
= true; 
4124     bool res 
= m_pState
->DoExpand(pwc
); 
4129             SendEvent( wxEVT_PG_ITEM_EXPANDED
, p 
); 
4131         RecalculateVirtualSize(); 
4135     m_pState
->m_dontCenterSplitter 
= prevDontCenterSplitter
; 
4140 // ----------------------------------------------------------------------- 
4142 bool wxPropertyGrid::DoHideProperty( wxPGProperty
* p
, bool hide
, int flags 
) 
4145         return m_pState
->DoHideProperty(p
, hide
, flags
); 
4147     wxArrayPGProperty selection 
= m_pState
->m_selection
;  // Must use a copy 
4148     int selRemoveCount 
= 0; 
4149     for ( unsigned int i
=0; i
<selection
.size(); i
++ ) 
4151         wxPGProperty
* selected 
= selection
[i
]; 
4152         if ( selected 
== p 
|| selected
->IsSomeParent(p
) ) 
4154             if ( !DoRemoveFromSelection(p
, flags
) ) 
4156             selRemoveCount 
+= 1; 
4160     m_pState
->DoHideProperty(p
, hide
, flags
); 
4162     RecalculateVirtualSize(); 
4169 // ----------------------------------------------------------------------- 
4170 // wxPropertyGrid size related methods 
4171 // ----------------------------------------------------------------------- 
4173 void wxPropertyGrid::RecalculateVirtualSize( int forceXPos 
) 
4175     if ( (m_iFlags 
& wxPG_FL_RECALCULATING_VIRTUAL_SIZE
) || m_frozen 
) 
4179     // If virtual height was changed, then recalculate editor control position(s) 
4180     if ( m_pState
->m_vhCalcPending 
) 
4181         CorrectEditorWidgetPosY(); 
4183     m_pState
->EnsureVirtualHeight(); 
4185     wxASSERT_LEVEL_2_MSG( 
4186         m_pState
->GetVirtualHeight() == m_pState
->GetActualVirtualHeight(), 
4187         "VirtualHeight and ActualVirtualHeight should match" 
4190     m_iFlags 
|= wxPG_FL_RECALCULATING_VIRTUAL_SIZE
; 
4192     int x 
= m_pState
->m_width
; 
4193     int y 
= m_pState
->m_virtualHeight
; 
4196     GetClientSize(&width
,&height
); 
4198     // Now adjust virtual size. 
4199     SetVirtualSize(x
, y
); 
4205     // Adjust scrollbars 
4206     if ( HasVirtualWidth() ) 
4208         xAmount 
= x
/wxPG_PIXELS_PER_UNIT
; 
4209         xPos 
= GetScrollPos( wxHORIZONTAL 
); 
4212     if ( forceXPos 
!= -1 ) 
4215     else if ( xPos 
> (xAmount
-(width
/wxPG_PIXELS_PER_UNIT
)) ) 
4218     int yAmount 
= y 
/ wxPG_PIXELS_PER_UNIT
; 
4219     int yPos 
= GetScrollPos( wxVERTICAL 
); 
4221     SetScrollbars( wxPG_PIXELS_PER_UNIT
, wxPG_PIXELS_PER_UNIT
, 
4222                    xAmount
, yAmount
, xPos
, yPos
, true ); 
4224     // Must re-get size now 
4225     GetClientSize(&width
,&height
); 
4227     if ( !HasVirtualWidth() ) 
4229         m_pState
->SetVirtualWidth(width
); 
4236     m_pState
->CheckColumnWidths(); 
4238     if ( GetSelection() ) 
4239         CorrectEditorWidgetSizeX(); 
4241     m_iFlags 
&= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE
; 
4244 // ----------------------------------------------------------------------- 
4246 void wxPropertyGrid::OnResize( wxSizeEvent
& event 
) 
4248     if ( !(m_iFlags 
& wxPG_FL_INITIALIZED
) ) 
4252     GetClientSize(&width
, &height
); 
4257 #if wxPG_DOUBLE_BUFFER 
4258     if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING
) ) 
4260         int dblh 
= (m_lineHeight
*2); 
4261         if ( !m_doubleBuffer 
) 
4263             // Create double buffer bitmap to draw on, if none 
4264             int w 
= (width
>250)?width
:250; 
4265             int h 
= height 
+ dblh
; 
4267             m_doubleBuffer 
= new wxBitmap( w
, h 
); 
4271             int w 
= m_doubleBuffer
->GetWidth(); 
4272             int h 
= m_doubleBuffer
->GetHeight(); 
4274             // Double buffer must be large enough 
4275             if ( w 
< width 
|| h 
< (height
+dblh
) ) 
4277                 if ( w 
< width 
) w 
= width
; 
4278                 if ( h 
< (height
+dblh
) ) h 
= height 
+ dblh
; 
4279                 delete m_doubleBuffer
; 
4280                 m_doubleBuffer 
= new wxBitmap( w
, h 
); 
4287     m_pState
->OnClientWidthChange( width
, event
.GetSize().x 
- m_ncWidth
, true ); 
4288     m_ncWidth 
= event
.GetSize().x
; 
4292         if ( m_pState
->m_itemsAdded 
) 
4293             PrepareAfterItemsAdded(); 
4295             // Without this, virtual size (atleast under wxGTK) will be skewed 
4296             RecalculateVirtualSize(); 
4302 // ----------------------------------------------------------------------- 
4304 void wxPropertyGrid::SetVirtualWidth( int width 
) 
4308         // Disable virtual width 
4309         width 
= GetClientSize().x
; 
4310         ClearInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH
); 
4314         // Enable virtual width 
4315         SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH
); 
4317     m_pState
->SetVirtualWidth( width 
); 
4320 void wxPropertyGrid::SetFocusOnCanvas() 
4322     SetFocusIgnoringChildren(); 
4323     m_editorFocused 
= 0; 
4326 // ----------------------------------------------------------------------- 
4327 // wxPropertyGrid mouse event handling 
4328 // ----------------------------------------------------------------------- 
4330 // selFlags uses same values DoSelectProperty's flags 
4331 // Returns true if event was vetoed. 
4332 bool wxPropertyGrid::SendEvent( int eventType
, wxPGProperty
* p
, 
4334                                 unsigned int selFlags
, 
4335                                 unsigned int column 
) 
4337     // Send property grid event of specific type and with specific property 
4338     wxPropertyGridEvent 
evt( eventType
, m_eventObject
->GetId() ); 
4339     evt
.SetPropertyGrid(this); 
4340     evt
.SetEventObject(m_eventObject
); 
4342     evt
.SetColumn(column
); 
4343     if ( eventType 
== wxEVT_PG_CHANGING 
) 
4346         evt
.SetCanVeto(true); 
4347         m_validationInfo
.m_pValue 
= pValue
; 
4348         evt
.SetupValidationInfo(); 
4353             evt
.SetPropertyValue(p
->GetValue()); 
4355         if ( !(selFlags 
& wxPG_SEL_NOVALIDATE
) ) 
4356             evt
.SetCanVeto(true); 
4359     m_processedEvent 
= &evt
; 
4360     m_eventObject
->HandleWindowEvent(evt
); 
4361     m_processedEvent 
= NULL
; 
4363     return evt
.WasVetoed(); 
4366 // ----------------------------------------------------------------------- 
4368 // Return false if should be skipped 
4369 bool wxPropertyGrid::HandleMouseClick( int x
, unsigned int y
, wxMouseEvent 
&event 
) 
4373     // Need to set focus? 
4374     if ( !(m_iFlags 
& wxPG_FL_FOCUSED
) ) 
4379     wxPropertyGridPageState
* state 
= m_pState
; 
4381     int splitterHitOffset
; 
4382     int columnHit 
= state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4384     wxPGProperty
* p 
= DoGetItemAtY(y
); 
4388         int depth 
= (int)p
->GetDepth() - 1; 
4390         int marginEnds 
= m_marginWidth 
+ ( depth 
* m_subgroup_extramargin 
); 
4392         if ( x 
>= marginEnds 
) 
4396             if ( p
->IsCategory() ) 
4398                 // This is category. 
4399                 wxPropertyCategory
* pwc 
= (wxPropertyCategory
*)p
; 
4401                 int textX 
= m_marginWidth 
+ ((unsigned int)((pwc
->m_depth
-1)*m_subgroup_extramargin
)); 
4403                 // Expand, collapse, activate etc. if click on text or left of splitter. 
4406                      ( x 
< (textX
+pwc
->GetTextExtent(this, m_captionFont
)+(wxPG_CAPRECTXMARGIN
*2)) || 
4411                     if ( !AddToSelectionFromInputEvent( p
, 
4416                     // On double-click, expand/collapse. 
4417                     if ( event
.ButtonDClick() && !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
4419                         if ( pwc
->IsExpanded() ) DoCollapse( p
, true ); 
4420                         else DoExpand( p
, true ); 
4424             else if ( splitterHit 
== -1 ) 
4427                 unsigned int selFlag 
= 0; 
4428                 if ( columnHit 
== 1 ) 
4430                     m_iFlags 
|= wxPG_FL_ACTIVATION_BY_CLICK
; 
4431                     selFlag 
= wxPG_SEL_FOCUS
; 
4433                 if ( !AddToSelectionFromInputEvent( p
, 
4439                 m_iFlags 
&= ~(wxPG_FL_ACTIVATION_BY_CLICK
); 
4441                 if ( p
->GetChildCount() && !p
->IsCategory() ) 
4442                     // On double-click, expand/collapse. 
4443                     if ( event
.ButtonDClick() && !(m_windowStyle 
& wxPG_HIDE_MARGIN
) ) 
4445                         wxPGProperty
* pwc 
= (wxPGProperty
*)p
; 
4446                         if ( pwc
->IsExpanded() ) DoCollapse( p
, true ); 
4447                         else DoExpand( p
, true ); 
4454             // click on splitter 
4455                 if ( !(m_windowStyle 
& wxPG_STATIC_SPLITTER
) ) 
4457                     if ( event
.GetEventType() == wxEVT_LEFT_DCLICK 
) 
4459                         // Double-clicking the splitter causes auto-centering 
4460                         CenterSplitter( true ); 
4462                     else if ( m_dragStatus 
== 0 ) 
4465                         // Begin draggin the splitter 
4469                         DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE
); 
4473                             // Changes must be committed here or the 
4474                             // value won't be drawn correctly 
4475                             if ( !CommitChangesFromEditor() ) 
4478                             m_wndEditor
->Show ( false ); 
4481                         if ( !(m_iFlags 
& wxPG_FL_MOUSE_CAPTURED
) ) 
4484                             m_iFlags 
|= wxPG_FL_MOUSE_CAPTURED
; 
4488                         m_draggedSplitter 
= splitterHit
; 
4489                         m_dragOffset 
= splitterHitOffset
; 
4491                     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
4492                         // Fixes button disappearance bug 
4494                             m_wndEditor2
->Show ( false ); 
4497                         m_startingSplitterX 
= x 
- splitterHitOffset
; 
4505             if ( p
->GetChildCount() ) 
4507                 int nx 
= x 
+ m_marginWidth 
- marginEnds
; // Normalize x. 
4509                 if ( (nx 
>= m_gutterWidth 
&& nx 
< (m_gutterWidth
+m_iconWidth
)) ) 
4511                     int y2 
= y 
% m_lineHeight
; 
4512                     if ( (y2 
>= m_buttonSpacingY 
&& y2 
< (m_buttonSpacingY
+m_iconHeight
)) ) 
4514                         // On click on expander button, expand/collapse 
4515                         if ( ((wxPGProperty
*)p
)->IsExpanded() ) 
4516                             DoCollapse( p
, true ); 
4518                             DoExpand( p
, true ); 
4527 // ----------------------------------------------------------------------- 
4529 bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x
), 
4530                                             unsigned int WXUNUSED(y
), 
4531                                             wxMouseEvent
& event 
) 
4535         // Select property here as well 
4536         wxPGProperty
* p 
= m_propHover
; 
4537         AddToSelectionFromInputEvent(p
, m_colHover
, &event
); 
4539         // Send right click event. 
4540         SendEvent( wxEVT_PG_RIGHT_CLICK
, p 
); 
4547 // ----------------------------------------------------------------------- 
4549 bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x
), 
4550                                              unsigned int WXUNUSED(y
), 
4551                                              wxMouseEvent
& event 
) 
4555         // Select property here as well 
4556         wxPGProperty
* p 
= m_propHover
; 
4558         AddToSelectionFromInputEvent(p
, m_colHover
, &event
); 
4560         // Send double-click event. 
4561         SendEvent( wxEVT_PG_DOUBLE_CLICK
, m_propHover 
); 
4568 // ----------------------------------------------------------------------- 
4570 #if wxPG_SUPPORT_TOOLTIPS 
4572 void wxPropertyGrid::SetToolTip( const wxString
& tipString 
) 
4574     if ( tipString
.length() ) 
4576         wxScrolledWindow::SetToolTip(tipString
); 
4580     #if wxPG_ALLOW_EMPTY_TOOLTIPS 
4581         wxScrolledWindow::SetToolTip( m_emptyString 
); 
4583         wxScrolledWindow::SetToolTip( NULL 
); 
4588 #endif // #if wxPG_SUPPORT_TOOLTIPS 
4590 // ----------------------------------------------------------------------- 
4592 // Return false if should be skipped 
4593 bool wxPropertyGrid::HandleMouseMove( int x
, unsigned int y
, 
4594                                       wxMouseEvent 
&event 
) 
4596     // Safety check (needed because mouse capturing may 
4597     // otherwise freeze the control) 
4598     if ( m_dragStatus 
> 0 && !event
.Dragging() ) 
4600         HandleMouseUp(x
, y
, event
); 
4603     wxPropertyGridPageState
* state 
= m_pState
; 
4605     int splitterHitOffset
; 
4606     int columnHit 
= state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4607     int splitterX 
= x 
- splitterHitOffset
; 
4609     m_colHover 
= columnHit
; 
4611     if ( m_dragStatus 
> 0 ) 
4613         if ( x 
> (m_marginWidth 
+ wxPG_DRAG_MARGIN
) && 
4614              x 
< (m_pState
->m_width 
- wxPG_DRAG_MARGIN
) ) 
4617             int newSplitterX 
= x 
- m_dragOffset
; 
4618             int splitterX 
= x 
- splitterHitOffset
; 
4620             // Splitter redraw required? 
4621             if ( newSplitterX 
!= splitterX 
) 
4624                 state
->m_dontCenterSplitter 
= true; 
4625                 state
->DoSetSplitterPosition(newSplitterX
, 
4628                 state
->m_fSplitterX 
= (float) newSplitterX
; 
4630                 if ( GetSelection() ) 
4631                     CorrectEditorWidgetSizeX(); 
4645         int ih 
= m_lineHeight
; 
4648     #if wxPG_SUPPORT_TOOLTIPS 
4649         wxPGProperty
* prevHover 
= m_propHover
; 
4650         unsigned char prevSide 
= m_mouseSide
; 
4652         int curPropHoverY 
= y 
- (y 
% ih
); 
4654         // On which item it hovers 
4657              ( sy 
< m_propHoverY 
|| sy 
>= (m_propHoverY
+ih
) ) 
4660             // Mouse moves on another property 
4662             m_propHover 
= DoGetItemAtY(y
); 
4663             m_propHoverY 
= curPropHoverY
; 
4666             SendEvent( wxEVT_PG_HIGHLIGHTED
, m_propHover 
); 
4669     #if wxPG_SUPPORT_TOOLTIPS 
4670         // Store which side we are on 
4672         if ( columnHit 
== 1 ) 
4674         else if ( columnHit 
== 0 ) 
4678         // If tooltips are enabled, show label or value as a tip 
4679         // in case it doesn't otherwise show in full length. 
4681         if ( m_windowStyle 
& wxPG_TOOLTIPS 
) 
4683             wxToolTip
* tooltip 
= GetToolTip(); 
4685             if ( m_propHover 
!= prevHover 
|| prevSide 
!= m_mouseSide 
) 
4687                 if ( m_propHover 
&& !m_propHover
->IsCategory() ) 
4690                     if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS 
) 
4692                         // Show help string as a tooltip 
4693                         wxString tipString 
= m_propHover
->GetHelpString(); 
4695                         SetToolTip(tipString
); 
4699                         // Show cropped value string as a tooltip 
4703                         if ( m_mouseSide 
== 1 ) 
4705                             tipString 
= m_propHover
->m_label
; 
4706                             space 
= splitterX
-m_marginWidth
-3; 
4708                         else if ( m_mouseSide 
== 2 ) 
4710                             tipString 
= m_propHover
->GetDisplayedString(); 
4712                             space 
= m_width 
- splitterX
; 
4713                             if ( m_propHover
->m_flags 
& wxPG_PROP_CUSTOMIMAGE 
) 
4714                                 space 
-= wxPG_CUSTOM_IMAGE_WIDTH 
+ 
4715                                          wxCC_CUSTOM_IMAGE_MARGIN1 
+ 
4716                                          wxCC_CUSTOM_IMAGE_MARGIN2
; 
4722                             GetTextExtent( tipString
, &tw
, &th
, 0, 0 ); 
4725                                 SetToolTip( tipString 
); 
4732                             #if wxPG_ALLOW_EMPTY_TOOLTIPS 
4733                                 SetToolTip( m_emptyString 
); 
4735                                 wxScrolledWindow::SetToolTip( NULL 
); 
4746                     #if wxPG_ALLOW_EMPTY_TOOLTIPS 
4747                         SetToolTip( m_emptyString 
); 
4749                         wxScrolledWindow::SetToolTip( NULL 
); 
4757         if ( splitterHit 
== -1 || 
4759              HasFlag(wxPG_STATIC_SPLITTER
) ) 
4761             // hovering on something else 
4762             if ( m_curcursor 
!= wxCURSOR_ARROW 
) 
4763                 CustomSetCursor( wxCURSOR_ARROW 
); 
4767             // Do not allow splitter cursor on caption items. 
4768             // (also not if we were dragging and its started 
4769             // outside the splitter region) 
4771             if ( !m_propHover
->IsCategory() && 
4775                 // hovering on splitter 
4777                 // NB: Condition disabled since MouseLeave event (from the 
4778                 //     editor control) cannot be reliably detected. 
4779                 //if ( m_curcursor != wxCURSOR_SIZEWE ) 
4780                 CustomSetCursor( wxCURSOR_SIZEWE
, true ); 
4786                 // hovering on something else 
4787                 if ( m_curcursor 
!= wxCURSOR_ARROW 
) 
4788                     CustomSetCursor( wxCURSOR_ARROW 
); 
4793         // Multi select by dragging 
4795         if ( (GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION
) && 
4796              event
.LeftIsDown() && 
4800              !state
->DoIsPropertySelected(m_propHover
) ) 
4802             // Additional requirement is that the hovered property 
4803             // is adjacent to edges of selection. 
4804             const wxArrayPGProperty
& selection 
= GetSelectedProperties(); 
4806             // Since categories cannot be selected along with 'other' 
4807             // properties, exclude them from iterator flags. 
4808             int iterFlags 
= wxPG_ITERATE_VISIBLE 
& (~wxPG_PROP_CATEGORY
); 
4810             for ( int i
=(selection
.size()-1); i
>=0; i
-- ) 
4812                 // TODO: This could be optimized by keeping track of 
4813                 //       which properties are at the edges of selection. 
4814                 wxPGProperty
* selProp 
= selection
[i
]; 
4815                 if ( state
->ArePropertiesAdjacent(m_propHover
, selProp
, 
4818                     DoAddToSelection(m_propHover
); 
4827 // ----------------------------------------------------------------------- 
4829 // Also handles Leaving event 
4830 bool wxPropertyGrid::HandleMouseUp( int x
, unsigned int WXUNUSED(y
), 
4831                                     wxMouseEvent 
&WXUNUSED(event
) ) 
4833     wxPropertyGridPageState
* state 
= m_pState
; 
4837     int splitterHitOffset
; 
4838     state
->HitTestH( x
, &splitterHit
, &splitterHitOffset 
); 
4840     // No event type check - basicly calling this method should 
4841     // just stop dragging. 
4842     // Left up after dragged? 
4843     if ( m_dragStatus 
>= 1 ) 
4846     // End Splitter Dragging 
4848         // DO NOT ENABLE FOLLOWING LINE! 
4849         // (it is only here as a reminder to not to do it) 
4852         // Disable splitter auto-centering 
4853         state
->m_dontCenterSplitter 
= true; 
4855         // This is necessary to return cursor 
4856         if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
4859             m_iFlags 
&= ~(wxPG_FL_MOUSE_CAPTURED
); 
4862         // Set back the default cursor, if necessary 
4863         if ( splitterHit 
== -1 || 
4866             CustomSetCursor( wxCURSOR_ARROW 
); 
4871         // Control background needs to be cleared 
4872         wxPGProperty
* selected 
= GetSelection(); 
4873         if ( !(m_iFlags 
& wxPG_FL_PRIMARY_FILLS_ENTIRE
) && selected 
) 
4874             DrawItem( selected 
); 
4878             m_wndEditor
->Show ( true ); 
4881     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT 
4882         // Fixes button disappearance bug 
4884             m_wndEditor2
->Show ( true ); 
4887         // This clears the focus. 
4888         m_editorFocused 
= 0; 
4894 // ----------------------------------------------------------------------- 
4896 bool wxPropertyGrid::OnMouseCommon( wxMouseEvent
& event
, int* px
, int* py 
) 
4898     int splitterX 
= GetSplitterPosition(); 
4901     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &ux
, &uy 
); 
4903     wxWindow
* wnd 
= GetEditorControl(); 
4905     // Hide popup on clicks 
4906     if ( event
.GetEventType() != wxEVT_MOTION 
) 
4907         if ( wnd 
&& wnd
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)) ) 
4909             ((wxOwnerDrawnComboBox
*)wnd
)->HidePopup(); 
4915     if ( wnd 
== NULL 
|| m_dragStatus 
|| 
4917            ux 
<= (splitterX 
+ wxPG_SPLITTERX_DETECTMARGIN2
) || 
4918            ux 
>= (r
.x
+r
.width
) || 
4920            event
.m_y 
>= (r
.y
+r
.height
) 
4930         if ( m_curcursor 
!= wxCURSOR_ARROW 
) CustomSetCursor ( wxCURSOR_ARROW 
); 
4935 // ----------------------------------------------------------------------- 
4937 void wxPropertyGrid::OnMouseClick( wxMouseEvent 
&event 
) 
4940     if ( OnMouseCommon( event
, &x
, &y 
) ) 
4942         HandleMouseClick(x
,y
,event
); 
4947 // ----------------------------------------------------------------------- 
4949 void wxPropertyGrid::OnMouseRightClick( wxMouseEvent 
&event 
) 
4952     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &x
, &y 
); 
4953     HandleMouseRightClick(x
,y
,event
); 
4957 // ----------------------------------------------------------------------- 
4959 void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent 
&event 
) 
4961     // Always run standard mouse-down handler as well 
4962     OnMouseClick(event
); 
4965     CalcUnscrolledPosition( event
.m_x
, event
.m_y
, &x
, &y 
); 
4966     HandleMouseDoubleClick(x
,y
,event
); 
4970 // ----------------------------------------------------------------------- 
4972 void wxPropertyGrid::OnMouseMove( wxMouseEvent 
&event 
) 
4975     if ( OnMouseCommon( event
, &x
, &y 
) ) 
4977         HandleMouseMove(x
,y
,event
); 
4982 // ----------------------------------------------------------------------- 
4984 void wxPropertyGrid::OnMouseUp( wxMouseEvent 
&event 
) 
4987     if ( OnMouseCommon( event
, &x
, &y 
) ) 
4989         HandleMouseUp(x
,y
,event
); 
4994 // ----------------------------------------------------------------------- 
4996 void wxPropertyGrid::OnMouseEntry( wxMouseEvent 
&event 
) 
4998     // This may get called from child control as well, so event's 
4999     // mouse position cannot be relied on. 
5001     if ( event
.Entering() ) 
5003         if ( !(m_iFlags 
& wxPG_FL_MOUSE_INSIDE
) ) 
5005             // TODO: Fix this (detect parent and only do 
5006             //   cursor trick if it is a manager). 
5007             wxASSERT( GetParent() ); 
5008             GetParent()->SetCursor(wxNullCursor
); 
5010             m_iFlags 
|= wxPG_FL_MOUSE_INSIDE
; 
5013             GetParent()->SetCursor(wxNullCursor
); 
5015     else if ( event
.Leaving() ) 
5017         // Without this, wxSpinCtrl editor will sometimes have wrong cursor 
5018         SetCursor( wxNullCursor 
); 
5020         // Get real cursor position 
5021         wxPoint pt 
= ScreenToClient(::wxGetMousePosition()); 
5023         if ( ( pt
.x 
<= 0 || pt
.y 
<= 0 || pt
.x 
>= m_width 
|| pt
.y 
>= m_height 
) ) 
5026                 if ( (m_iFlags 
& wxPG_FL_MOUSE_INSIDE
) ) 
5028                     m_iFlags 
&= ~(wxPG_FL_MOUSE_INSIDE
); 
5032                     wxPropertyGrid::HandleMouseUp ( -1, 10000, event 
); 
5040 // ----------------------------------------------------------------------- 
5042 // Common code used by various OnMouseXXXChild methods. 
5043 bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent 
&event
, int* px
, int *py 
) 
5045     wxWindow
* topCtrlWnd 
= (wxWindow
*)event
.GetEventObject(); 
5046     wxASSERT( topCtrlWnd 
); 
5048     event
.GetPosition(&x
,&y
); 
5050     int splitterX 
= GetSplitterPosition(); 
5052     wxRect r 
= topCtrlWnd
->GetRect(); 
5053     if ( !m_dragStatus 
&& 
5054          x 
> (splitterX
-r
.x
+wxPG_SPLITTERX_DETECTMARGIN2
) && 
5055          y 
>= 0 && y 
< r
.height \
 
5058         if ( m_curcursor 
!= wxCURSOR_ARROW 
) CustomSetCursor ( wxCURSOR_ARROW 
); 
5063         CalcUnscrolledPosition( event
.m_x 
+ r
.x
, event
.m_y 
+ r
.y
, \
 
5070 void wxPropertyGrid::OnMouseClickChild( wxMouseEvent 
&event 
) 
5073     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5075         bool res 
= HandleMouseClick(x
,y
,event
); 
5076         if ( !res 
) event
.Skip(); 
5080 void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent 
&event 
) 
5083     wxASSERT( m_wndEditor 
); 
5084     // These coords may not be exact (about +-2), 
5085     // but that should not matter (right click is about item, not position). 
5086     wxPoint pt 
= m_wndEditor
->GetPosition(); 
5087     CalcUnscrolledPosition( event
.m_x 
+ pt
.x
, event
.m_y 
+ pt
.y
, &x
, &y 
); 
5089     // FIXME: Used to set m_propHover to selection here. Was it really 
5092     bool res 
= HandleMouseRightClick(x
,y
,event
); 
5093     if ( !res 
) event
.Skip(); 
5096 void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent 
&event 
) 
5099     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5101         bool res 
= HandleMouseMove(x
,y
,event
); 
5102         if ( !res 
) event
.Skip(); 
5106 void wxPropertyGrid::OnMouseUpChild( wxMouseEvent 
&event 
) 
5109     if ( OnMouseChildCommon(event
,&x
,&y
) ) 
5111         bool res 
= HandleMouseUp(x
,y
,event
); 
5112         if ( !res 
) event
.Skip(); 
5116 // ----------------------------------------------------------------------- 
5117 // wxPropertyGrid keyboard event handling 
5118 // ----------------------------------------------------------------------- 
5120 int wxPropertyGrid::KeyEventToActions(wxKeyEvent 
&event
, int* pSecond
) const 
5122     // Translates wxKeyEvent to wxPG_ACTION_XXX 
5124     int keycode 
= event
.GetKeyCode(); 
5125     int modifiers 
= event
.GetModifiers(); 
5127     wxASSERT( !(modifiers
&~(0xFFFF)) ); 
5129     int hashMapKey 
= (keycode 
& 0xFFFF) | ((modifiers 
& 0xFFFF) << 16); 
5131     wxPGHashMapI2I::const_iterator it 
= m_actionTriggers
.find(hashMapKey
); 
5133     if ( it 
== m_actionTriggers
.end() ) 
5138         int second 
= (it
->second
>>16) & 0xFFFF; 
5142     return (it
->second 
& 0xFFFF); 
5145 void wxPropertyGrid::AddActionTrigger( int action
, int keycode
, int modifiers 
) 
5147     wxASSERT( !(modifiers
&~(0xFFFF)) ); 
5149     int hashMapKey 
= (keycode 
& 0xFFFF) | ((modifiers 
& 0xFFFF) << 16); 
5151     wxPGHashMapI2I::iterator it 
= m_actionTriggers
.find(hashMapKey
); 
5153     if ( it 
!= m_actionTriggers
.end() ) 
5155         // This key combination is already used 
5157         // Can add secondary? 
5158         wxASSERT_MSG( !(it
->second
&~(0xFFFF)), 
5159                       wxT("You can only add up to two separate actions per key combination.") ); 
5161         action 
= it
->second 
| (action
<<16); 
5164     m_actionTriggers
[hashMapKey
] = action
; 
5167 void wxPropertyGrid::ClearActionTriggers( int action 
) 
5169     wxPGHashMapI2I::iterator it
; 
5174         didSomething 
= false; 
5176         for ( it 
= m_actionTriggers
.begin(); 
5177               it 
!= m_actionTriggers
.end(); 
5180             if ( it
->second 
== action 
) 
5182                 m_actionTriggers
.erase(it
); 
5183                 didSomething 
= true; 
5188     while ( didSomething 
); 
5191 void wxPropertyGrid::HandleKeyEvent( wxKeyEvent 
&event
, bool fromChild 
) 
5194     // Handles key event when editor control is not focused. 
5197     wxCHECK2(!m_frozen
, return); 
5199     // Travelsal between items, collapsing/expanding, etc. 
5200     wxPGProperty
* selected 
= GetSelection(); 
5201     int keycode 
= event
.GetKeyCode(); 
5202     bool editorFocused 
= IsEditorFocused(); 
5204     if ( keycode 
== WXK_TAB 
) 
5206         wxWindow
* mainControl
; 
5208         if ( HasInternalFlag(wxPG_FL_IN_MANAGER
) ) 
5209             mainControl 
= GetParent(); 
5213         if ( !event
.ShiftDown() ) 
5215             if ( !editorFocused 
&& m_wndEditor 
) 
5217                 DoSelectProperty( selected
, wxPG_SEL_FOCUS 
); 
5221                 // Tab traversal workaround for platforms on which 
5222                 // wxWindow::Navigate() may navigate into first child 
5223                 // instead of next sibling. Does not work perfectly 
5224                 // in every scenario (for instance, when property grid 
5225                 // is either first or last control). 
5226             #if defined(__WXGTK__) 
5227                 wxWindow
* sibling 
= mainControl
->GetNextSibling(); 
5229                     sibling
->SetFocusFromKbd(); 
5231                 Navigate(wxNavigationKeyEvent::IsForward
); 
5237             if ( editorFocused 
) 
5243             #if defined(__WXGTK__) 
5244                 wxWindow
* sibling 
= mainControl
->GetPrevSibling(); 
5246                     sibling
->SetFocusFromKbd(); 
5248                 Navigate(wxNavigationKeyEvent::IsBackward
); 
5256     // Ignore Alt and Control when they are down alone 
5257     if ( keycode 
== WXK_ALT 
|| 
5258          keycode 
== WXK_CONTROL 
) 
5265     int action 
= KeyEventToActions(event
, &secondAction
); 
5267     if ( editorFocused 
&& action 
== wxPG_ACTION_CANCEL_EDIT 
) 
5270         // Esc cancels any changes 
5271         if ( IsEditorsValueModified() ) 
5273             EditorsValueWasNotModified(); 
5275             // Update the control as well 
5276             selected
->GetEditorClass()-> 
5277                 SetControlStringValue( selected
, 
5279                                        selected
->GetDisplayedString() ); 
5282         OnValidationFailureReset(selected
); 
5288     // Except for TAB and ESC, handle child control events in child control 
5291         // Only propagate event if it had modifiers 
5292         if ( !event
.HasModifiers() ) 
5294             event
.StopPropagation(); 
5300     bool wasHandled 
= false; 
5305         if ( ButtonTriggerKeyTest(action
, event
) ) 
5308         wxPGProperty
* p 
= selected
; 
5310         // Travel and expand/collapse 
5313         if ( p
->GetChildCount() ) 
5315             if ( action 
== wxPG_ACTION_COLLAPSE_PROPERTY 
|| secondAction 
== wxPG_ACTION_COLLAPSE_PROPERTY 
) 
5317                 if ( (m_windowStyle 
& wxPG_HIDE_MARGIN
) || Collapse(p
) ) 
5320             else if ( action 
== wxPG_ACTION_EXPAND_PROPERTY 
|| secondAction 
== wxPG_ACTION_EXPAND_PROPERTY 
) 
5322                 if ( (m_windowStyle 
& wxPG_HIDE_MARGIN
) || Expand(p
) ) 
5329             if ( action 
== wxPG_ACTION_PREV_PROPERTY 
|| secondAction 
== wxPG_ACTION_PREV_PROPERTY 
) 
5333             else if ( action 
== wxPG_ACTION_NEXT_PROPERTY 
|| secondAction 
== wxPG_ACTION_NEXT_PROPERTY 
) 
5339         if ( selectDir 
>= -1 ) 
5341             p 
= wxPropertyGridIterator::OneStep( m_pState
, wxPG_ITERATE_VISIBLE
, p
, selectDir 
); 
5343                 DoSelectProperty(p
); 
5349         // If nothing was selected, select the first item now 
5350         // (or navigate out of tab). 
5351         if ( action 
!= wxPG_ACTION_CANCEL_EDIT 
&& secondAction 
!= wxPG_ACTION_CANCEL_EDIT 
) 
5353             wxPGProperty
* p 
= wxPropertyGridInterface::GetFirst(); 
5354             if ( p 
) DoSelectProperty(p
); 
5363 // ----------------------------------------------------------------------- 
5365 void wxPropertyGrid::OnKey( wxKeyEvent 
&event 
) 
5367     // If there was editor open and focused, then this event should not 
5368     // really be processed here. 
5369     if ( IsEditorFocused() ) 
5371         // However, if event had modifiers, it is probably still best 
5373         if ( event
.HasModifiers() ) 
5376             event
.StopPropagation(); 
5380     HandleKeyEvent(event
, false); 
5383 // ----------------------------------------------------------------------- 
5385 bool wxPropertyGrid::ButtonTriggerKeyTest( int action
, wxKeyEvent
& event 
) 
5390         action 
= KeyEventToActions(event
, &secondAction
); 
5393     // Does the keycode trigger button? 
5394     if ( action 
== wxPG_ACTION_PRESS_BUTTON 
&& 
5397         wxCommandEvent 
evt(wxEVT_COMMAND_BUTTON_CLICKED
, m_wndEditor2
->GetId()); 
5398         GetEventHandler()->AddPendingEvent(evt
); 
5405 // ----------------------------------------------------------------------- 
5407 void wxPropertyGrid::OnChildKeyDown( wxKeyEvent 
&event 
) 
5409     HandleKeyEvent(event
, true); 
5412 // ----------------------------------------------------------------------- 
5413 // wxPropertyGrid miscellaneous event handling 
5414 // ----------------------------------------------------------------------- 
5416 void wxPropertyGrid::OnIdle( wxIdleEvent
& WXUNUSED(event
) ) 
5419     // Check if the focus is in this control or one of its children 
5420     wxWindow
* newFocused 
= wxWindow::FindFocus(); 
5422     if ( newFocused 
!= m_curFocused 
) 
5423         HandleFocusChange( newFocused 
); 
5426     // Check if top-level parent has changed 
5427     if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING 
) 
5429         wxWindow
* tlp 
= ::wxGetTopLevelParent(this); 
5435 bool wxPropertyGrid::IsEditorFocused() const 
5437     wxWindow
* focus 
= wxWindow::FindFocus(); 
5439     if ( focus 
== m_wndEditor 
|| focus 
== m_wndEditor2 
|| 
5440          focus 
== GetEditorControl() ) 
5446 // Called by focus event handlers. newFocused is the window that becomes focused. 
5447 void wxPropertyGrid::HandleFocusChange( wxWindow
* newFocused 
) 
5449     unsigned int oldFlags 
= m_iFlags
; 
5451     m_iFlags 
&= ~(wxPG_FL_FOCUSED
); 
5453     wxWindow
* parent 
= newFocused
; 
5455     // This must be one of nextFocus' parents. 
5458         // Use m_eventObject, which is either wxPropertyGrid or 
5459         // wxPropertyGridManager, as appropriate. 
5460         if ( parent 
== m_eventObject 
) 
5462             m_iFlags 
|= wxPG_FL_FOCUSED
; 
5465         parent 
= parent
->GetParent(); 
5468     m_curFocused 
= newFocused
; 
5470     if ( (m_iFlags 
& wxPG_FL_FOCUSED
) != 
5471          (oldFlags 
& wxPG_FL_FOCUSED
) ) 
5473         if ( !(m_iFlags 
& wxPG_FL_FOCUSED
) ) 
5475             // Need to store changed value 
5476             CommitChangesFromEditor(); 
5482             // Preliminary code for tab-order respecting 
5483             // tab-traversal (but should be moved to 
5486             wxWindow* prevFocus = event.GetWindow(); 
5487             wxWindow* useThis = this; 
5488             if ( m_iFlags & wxPG_FL_IN_MANAGER ) 
5489                 useThis = GetParent(); 
5492                  prevFocus->GetParent() == useThis->GetParent() ) 
5494                 wxList& children = useThis->GetParent()->GetChildren(); 
5496                 wxNode* node = children.Find(prevFocus); 
5498                 if ( node->GetNext() && 
5499                      useThis == node->GetNext()->GetData() ) 
5500                     DoSelectProperty(GetFirst()); 
5501                 else if ( node->GetPrevious () && 
5502                           useThis == node->GetPrevious()->GetData() ) 
5503                     DoSelectProperty(GetLastProperty()); 
5510         wxPGProperty
* selected 
= GetSelection(); 
5511         if ( selected 
&& (m_iFlags 
& wxPG_FL_INITIALIZED
) ) 
5512             DrawItem( selected 
); 
5516 void wxPropertyGrid::OnFocusEvent( wxFocusEvent
& event 
) 
5518     if ( event
.GetEventType() == wxEVT_SET_FOCUS 
) 
5519         HandleFocusChange((wxWindow
*)event
.GetEventObject()); 
5520     // Line changed to "else" when applying wxPropertyGrid patch #1675902 
5521     //else if ( event.GetWindow() ) 
5523         HandleFocusChange(event
.GetWindow()); 
5528 // ----------------------------------------------------------------------- 
5530 void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent
& event 
) 
5532     HandleFocusChange((wxWindow
*)event
.GetEventObject()); 
5536 // ----------------------------------------------------------------------- 
5538 void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent 
&event 
) 
5540     m_iFlags 
|= wxPG_FL_SCROLLED
; 
5545 // ----------------------------------------------------------------------- 
5547 void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent
& WXUNUSED(event
) ) 
5549     if ( m_iFlags 
& wxPG_FL_MOUSE_CAPTURED 
) 
5551         m_iFlags 
&= ~(wxPG_FL_MOUSE_CAPTURED
); 
5555 // ----------------------------------------------------------------------- 
5556 // Property editor related functions 
5557 // ----------------------------------------------------------------------- 
5559 // noDefCheck = true prevents infinite recursion. 
5560 wxPGEditor
* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor
* editorClass
, 
5561                                                    const wxString
& editorName
, 
5564     wxASSERT( editorClass 
); 
5566     if ( !noDefCheck 
&& wxPGGlobalVars
->m_mapEditorClasses
.empty() ) 
5567         RegisterDefaultEditors(); 
5569     wxString name 
= editorName
; 
5570     if ( name
.length() == 0 ) 
5571         name 
= editorClass
->GetName(); 
5573     // Existing editor under this name? 
5574     wxPGHashMapS2P::iterator vt_it 
= wxPGGlobalVars
->m_mapEditorClasses
.find(name
); 
5576     if ( vt_it 
!= wxPGGlobalVars
->m_mapEditorClasses
.end() ) 
5578         // If this name was already used, try class name. 
5579         name 
= editorClass
->GetClassInfo()->GetClassName(); 
5580         vt_it 
= wxPGGlobalVars
->m_mapEditorClasses
.find(name
); 
5583     wxCHECK_MSG( vt_it 
== wxPGGlobalVars
->m_mapEditorClasses
.end(), 
5584                  (wxPGEditor
*) vt_it
->second
, 
5585                  "Editor with given name was already registered" ); 
5587     wxPGGlobalVars
->m_mapEditorClasses
[name
] = (void*)editorClass
; 
5592 // Use this in RegisterDefaultEditors. 
5593 #define wxPGRegisterDefaultEditorClass(EDITOR) \ 
5594     if ( wxPGEditor_##EDITOR == NULL ) \ 
5596         wxPGEditor_##EDITOR = wxPropertyGrid::RegisterEditorClass( \ 
5597             new wxPG##EDITOR##Editor, true ); \ 
5600 // Registers all default editor classes 
5601 void wxPropertyGrid::RegisterDefaultEditors() 
5603     wxPGRegisterDefaultEditorClass( TextCtrl 
); 
5604     wxPGRegisterDefaultEditorClass( Choice 
); 
5605     wxPGRegisterDefaultEditorClass( ComboBox 
); 
5606     wxPGRegisterDefaultEditorClass( TextCtrlAndButton 
); 
5607 #if wxPG_INCLUDE_CHECKBOX 
5608     wxPGRegisterDefaultEditorClass( CheckBox 
); 
5610     wxPGRegisterDefaultEditorClass( ChoiceAndButton 
); 
5612     // Register SpinCtrl etc. editors before use 
5613     RegisterAdditionalEditors(); 
5616 // ----------------------------------------------------------------------- 
5617 // wxPGStringTokenizer 
5618 //   Needed to handle C-style string lists (e.g. "str1" "str2") 
5619 // ----------------------------------------------------------------------- 
5621 wxPGStringTokenizer::wxPGStringTokenizer( const wxString
& str
, wxChar delimeter 
) 
5622     : m_str(&str
), m_curPos(str
.begin()), m_delimeter(delimeter
) 
5626 wxPGStringTokenizer::~wxPGStringTokenizer() 
5630 bool wxPGStringTokenizer::HasMoreTokens() 
5632     const wxString
& str 
= *m_str
; 
5634     wxString::const_iterator i 
= m_curPos
; 
5636     wxUniChar delim 
= m_delimeter
; 
5638     wxUniChar prev_a 
= wxT('\0'); 
5640     bool inToken 
= false; 
5642     while ( i 
!= str
.end() ) 
5651                 m_readyToken
.clear(); 
5656             if ( prev_a 
!= wxT('\\') ) 
5660                     if ( a 
!= wxT('\\') ) 
5680     m_curPos 
= str
.end(); 
5688 wxString 
wxPGStringTokenizer::GetNextToken() 
5690     return m_readyToken
; 
5693 // ----------------------------------------------------------------------- 
5695 // ----------------------------------------------------------------------- 
5697 wxPGChoiceEntry::wxPGChoiceEntry() 
5698     : wxPGCell(), m_value(wxPG_INVALID_VALUE
) 
5702 // ----------------------------------------------------------------------- 
5704 // ----------------------------------------------------------------------- 
5706 wxPGChoicesData::wxPGChoicesData() 
5710 wxPGChoicesData::~wxPGChoicesData() 
5715 void wxPGChoicesData::Clear() 
5720 void wxPGChoicesData::CopyDataFrom( wxPGChoicesData
* data 
) 
5722     wxASSERT( m_items
.size() == 0 ); 
5724     m_items 
= data
->m_items
; 
5727 wxPGChoiceEntry
& wxPGChoicesData::Insert( int index
, 
5728                                           const wxPGChoiceEntry
& item 
) 
5730     wxVector
<wxPGChoiceEntry
>::iterator it
; 
5734         index 
= (int) m_items
.size(); 
5738         it 
= m_items
.begin() + index
; 
5741     m_items
.insert(it
, item
); 
5743     wxPGChoiceEntry
& ownEntry 
= m_items
[index
]; 
5745     // Need to fix value? 
5746     if ( ownEntry
.GetValue() == wxPG_INVALID_VALUE 
) 
5747         ownEntry
.SetValue(index
); 
5752 // ----------------------------------------------------------------------- 
5753 // wxPropertyGridEvent 
5754 // ----------------------------------------------------------------------- 
5756 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent
, wxCommandEvent
) 
5759 wxDEFINE_EVENT( wxEVT_PG_SELECTED
, wxPropertyGridEvent 
); 
5760 wxDEFINE_EVENT( wxEVT_PG_CHANGING
, wxPropertyGridEvent 
); 
5761 wxDEFINE_EVENT( wxEVT_PG_CHANGED
, wxPropertyGridEvent 
); 
5762 wxDEFINE_EVENT( wxEVT_PG_HIGHLIGHTED
, wxPropertyGridEvent 
); 
5763 wxDEFINE_EVENT( wxEVT_PG_RIGHT_CLICK
, wxPropertyGridEvent 
); 
5764 wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED
, wxPropertyGridEvent 
); 
5765 wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED
, wxPropertyGridEvent 
); 
5766 wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED
, wxPropertyGridEvent 
); 
5767 wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK
, wxPropertyGridEvent 
); 
5768 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN
, wxPropertyGridEvent 
); 
5769 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING
, wxPropertyGridEvent 
); 
5771 // ----------------------------------------------------------------------- 
5773 void wxPropertyGridEvent::Init() 
5775     m_validationInfo 
= NULL
; 
5778     m_wasVetoed 
= false; 
5781 // ----------------------------------------------------------------------- 
5783 wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType
, int id
) 
5784     : wxCommandEvent(commandType
,id
) 
5790 // ----------------------------------------------------------------------- 
5792 wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent
& event
) 
5793     : wxCommandEvent(event
) 
5795     m_eventType 
= event
.GetEventType(); 
5796     m_eventObject 
= event
.m_eventObject
; 
5798     OnPropertyGridSet(); 
5799     m_property 
= event
.m_property
; 
5800     m_validationInfo 
= event
.m_validationInfo
; 
5801     m_canVeto 
= event
.m_canVeto
; 
5802     m_wasVetoed 
= event
.m_wasVetoed
; 
5805 // ----------------------------------------------------------------------- 
5807 void wxPropertyGridEvent::OnPropertyGridSet() 
5813     wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
5815     m_pg
->m_liveEvents
.push_back(this); 
5818 // ----------------------------------------------------------------------- 
5820 wxPropertyGridEvent::~wxPropertyGridEvent() 
5825         wxCriticalSectionLocker(wxPGGlobalVars
->m_critSect
); 
5828         // Use iterate from the back since it is more likely that the event 
5829         // being desroyed is at the end of the array. 
5830         wxVector
<wxPropertyGridEvent
*>& liveEvents 
= m_pg
->m_liveEvents
; 
5832         for ( int i 
= liveEvents
.size()-1; i 
>= 0; i
-- ) 
5834             if ( liveEvents
[i
] == this ) 
5836                 liveEvents
.erase(liveEvents
.begin() + i
); 
5843 // ----------------------------------------------------------------------- 
5845 wxEvent
* wxPropertyGridEvent::Clone() const 
5847     return new wxPropertyGridEvent( *this ); 
5850 // ----------------------------------------------------------------------- 
5851 // wxPropertyGridPopulator 
5852 // ----------------------------------------------------------------------- 
5854 wxPropertyGridPopulator::wxPropertyGridPopulator() 
5858     wxPGGlobalVars
->m_offline
++; 
5861 // ----------------------------------------------------------------------- 
5863 void wxPropertyGridPopulator::SetState( wxPropertyGridPageState
* state 
) 
5866     m_propHierarchy
.clear(); 
5869 // ----------------------------------------------------------------------- 
5871 void wxPropertyGridPopulator::SetGrid( wxPropertyGrid
* pg 
) 
5877 // ----------------------------------------------------------------------- 
5879 wxPropertyGridPopulator::~wxPropertyGridPopulator() 
5882     // Free unused sets of choices 
5883     wxPGHashMapS2P::iterator it
; 
5885     for( it 
= m_dictIdChoices
.begin(); it 
!= m_dictIdChoices
.end(); ++it 
) 
5887         wxPGChoicesData
* data 
= (wxPGChoicesData
*) it
->second
; 
5894         m_pg
->GetPanel()->Refresh(); 
5896     wxPGGlobalVars
->m_offline
--; 
5899 // ----------------------------------------------------------------------- 
5901 wxPGProperty
* wxPropertyGridPopulator::Add( const wxString
& propClass
, 
5902                                             const wxString
& propLabel
, 
5903                                             const wxString
& propName
, 
5904                                             const wxString
* propValue
, 
5905                                             wxPGChoices
* pChoices 
) 
5907     wxClassInfo
* classInfo 
= wxClassInfo::FindClass(propClass
); 
5908     wxPGProperty
* parent 
= GetCurParent(); 
5910     if ( parent
->HasFlag(wxPG_PROP_AGGREGATE
) ) 
5912         ProcessError(wxString::Format(wxT("new children cannot be added to '%s'"),parent
->GetName().c_str())); 
5916     if ( !classInfo 
|| !classInfo
->IsKindOf(CLASSINFO(wxPGProperty
)) ) 
5918         ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass
.c_str())); 
5922     wxPGProperty
* property 
= (wxPGProperty
*) classInfo
->CreateObject(); 
5924     property
->SetLabel(propLabel
); 
5925     property
->DoSetName(propName
); 
5927     if ( pChoices 
&& pChoices
->IsOk() ) 
5928         property
->SetChoices(*pChoices
); 
5930     m_state
->DoInsert(parent
, -1, property
); 
5933         property
->SetValueFromString( *propValue
, wxPG_FULL_VALUE
| 
5934                                                   wxPG_PROGRAMMATIC_VALUE 
); 
5939 // ----------------------------------------------------------------------- 
5941 void wxPropertyGridPopulator::AddChildren( wxPGProperty
* property 
) 
5943     m_propHierarchy
.push_back(property
); 
5944     DoScanForChildren(); 
5945     m_propHierarchy
.pop_back(); 
5948 // ----------------------------------------------------------------------- 
5950 wxPGChoices 
wxPropertyGridPopulator::ParseChoices( const wxString
& choicesString
, 
5951                                                    const wxString
& idString 
) 
5953     wxPGChoices choices
; 
5956     if ( choicesString
[0] == wxT('@') ) 
5958         wxString ids 
= choicesString
.substr(1); 
5959         wxPGHashMapS2P::iterator it 
= m_dictIdChoices
.find(ids
); 
5960         if ( it 
== m_dictIdChoices
.end() ) 
5961             ProcessError(wxString::Format(wxT("No choices defined for id '%s'"),ids
.c_str())); 
5963             choices
.AssignData((wxPGChoicesData
*)it
->second
); 
5968         if ( idString
.length() ) 
5970             wxPGHashMapS2P::iterator it 
= m_dictIdChoices
.find(idString
); 
5971             if ( it 
!= m_dictIdChoices
.end() ) 
5973                 choices
.AssignData((wxPGChoicesData
*)it
->second
); 
5980             // Parse choices string 
5981             wxString::const_iterator it 
= choicesString
.begin(); 
5985             bool labelValid 
= false; 
5987             for ( ; it 
!= choicesString
.end(); ++it 
) 
5993                     if ( c 
== wxT('"') ) 
5998                             if ( !value
.ToLong(&l
, 0) ) l 
= wxPG_INVALID_VALUE
; 
5999                             choices
.Add(label
, l
); 
6002                         //wxLogDebug(wxT("%s, %s"),label.c_str(),value.c_str()); 
6007                     else if ( c 
== wxT('=') ) 
6014                     else if ( state 
== 2 && (wxIsalnum(c
) || c 
== wxT('x')) ) 
6021                     if ( c 
== wxT('"') ) 
6034                 if ( !value
.ToLong(&l
, 0) ) l 
= wxPG_INVALID_VALUE
; 
6035                 choices
.Add(label
, l
); 
6038             if ( !choices
.IsOk() ) 
6040                 choices
.EnsureData(); 
6044             if ( idString
.length() ) 
6045                 m_dictIdChoices
[idString
] = choices
.GetData(); 
6052 // ----------------------------------------------------------------------- 
6054 bool wxPropertyGridPopulator::ToLongPCT( const wxString
& s
, long* pval
, long max 
) 
6056     if ( s
.Last() == wxT('%') ) 
6058         wxString s2 
= s
.substr(0,s
.length()-1); 
6060         if ( s2
.ToLong(&val
, 10) ) 
6062             *pval 
= (val
*max
)/100; 
6068     return s
.ToLong(pval
, 10); 
6071 // ----------------------------------------------------------------------- 
6073 bool wxPropertyGridPopulator::AddAttribute( const wxString
& name
, 
6074                                             const wxString
& type
, 
6075                                             const wxString
& value 
) 
6077     int l 
= m_propHierarchy
.size(); 
6081     wxPGProperty
* p 
= m_propHierarchy
[l
-1]; 
6082     wxString valuel 
= value
.Lower(); 
6085     if ( type
.length() == 0 ) 
6090         if ( valuel 
== wxT("true") || valuel 
== wxT("yes") || valuel 
== wxT("1") ) 
6092         else if ( valuel 
== wxT("false") || valuel 
== wxT("no") || valuel 
== wxT("0") ) 
6094         else if ( value
.ToLong(&v
, 0) ) 
6101         if ( type 
== wxT("string") ) 
6105         else if ( type 
== wxT("int") ) 
6108             value
.ToLong(&v
, 0); 
6111         else if ( type 
== wxT("bool") ) 
6113             if ( valuel 
== wxT("true") || valuel 
== wxT("yes") || valuel 
== wxT("1") ) 
6120             ProcessError(wxString::Format(wxT("Invalid attribute type '%s'"),type
.c_str())); 
6125     p
->SetAttribute( name
, variant 
); 
6130 // ----------------------------------------------------------------------- 
6132 void wxPropertyGridPopulator::ProcessError( const wxString
& msg 
) 
6134     wxLogError(_("Error in resource: %s"),msg
.c_str()); 
6137 // ----------------------------------------------------------------------- 
6139 #endif  // wxUSE_PROPGRID