1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        propgrid.cpp
3 // Purpose:     wxPropertyGrid
4 // Author:      Jaakko Salli
5 // Modified by:
6 // Created:     Sep-25-2004
7 // RCS-ID:      $Id:
8 // Copyright:   (c) Jaakko Salli
9 // Licence:     wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16     #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20     #include "wx/defs.h"
21     #include "wx/object.h"
22     #include "wx/hash.h"
23     #include "wx/string.h"
24     #include "wx/log.h"
25     #include "wx/event.h"
26     #include "wx/window.h"
27     #include "wx/panel.h"
28     #include "wx/dc.h"
29     #include "wx/dcmemory.h"
30     #include "wx/button.h"
31     #include "wx/pen.h"
32     #include "wx/brush.h"
33     #include "wx/cursor.h"
34     #include "wx/dialog.h"
35     #include "wx/settings.h"
36     #include "wx/msgdlg.h"
37     #include "wx/choice.h"
38     #include "wx/stattext.h"
39     #include "wx/scrolwin.h"
40     #include "wx/dirdlg.h"
41     #include "wx/layout.h"
42     #include "wx/sizer.h"
43     #include "wx/textdlg.h"
44     #include "wx/filedlg.h"
45     #include "wx/statusbr.h"
46     #include "wx/intl.h"
47     #include "wx/frame.h"
48 #endif
49 
50 
51 // This define is necessary to prevent macro clearing
52 #define __wxPG_SOURCE_FILE__
53 
54 #include <wx/propgrid/propgrid.h>
55 #include <wx/propgrid/propdev.h>
56 #include <wx/propgrid/editors.h>
57 
58 #ifdef __WXPYTHON__
59     #include <wx/propgrid/advprops.h>
60     #include <wx/propgrid/extras.h>
61 #endif
62 
63 #if wxPG_USE_RENDERER_NATIVE
64     #include <wx/renderer.h>
65 #endif
66 
67 #if wxPG_USING_WXOWNERDRAWNCOMBOBOX
68     #include <wx/odcombo.h>
69 #else
70     #include <wx/propgrid/odcombo.h>
71 #endif
72 
73 #include "wx/timer.h"
74 #include "wx/dcbuffer.h"
75 #include "wx/image.h"
76 #include <wx/clipbrd.h>
77 #include <wx/dataobj.h>
78 
79 #ifdef __WXMSW__
80     #include <wx/msw/private.h>
81 #endif
82 
83 
84 // Two pics for the expand / collapse buttons.
85 // Files are not supplied with this project (since it is
86 // recommended to use either custom or native rendering).
87 // If you want them, get wxTreeMultiCtrl by Jorgen Bodde,
88 // and copy xpm files from archive to wxPropertyGrid src directory
89 // (and also comment/undef wxPG_ICON_WIDTH in propGrid.h
90 // and set wxPG_USE_RENDERER_NATIVE to 0).
91 #ifndef wxPG_ICON_WIDTH
92   #if defined(__WXMAC__)
93     #include "mac_collapse.xpm"
94     #include "mac_expand.xpm"
95   #elif defined(__WXGTK__)
96     #include "linux_collapse.xpm"
97     #include "linux_expand.xpm"
98   #else
99     #include "default_collapse.xpm"
100     #include "default_expand.xpm"
101   #endif
102 #endif
103 
104 
105 //#define wxPG_TEXT_INDENT                4 // For the wxComboControl
106 #define wxPG_ALLOW_CLIPPING             1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored
107 #define wxPG_GUTTER_DIV                 3 // gutter is max(iconwidth/gutter_div,gutter_min)
108 #define wxPG_GUTTER_MIN                 3 // gutter before and after image of [+] or [-]
109 #define wxPG_YSPACING_MIN               1
110 #define wxPG_DEFAULT_VSPACING           2 // This matches .NET propertygrid's value,
111                                           // but causes normal combobox to spill out under MSW
112 
113 #define wxPG_OPTIMAL_WIDTH              200 // Arbitrary
114 
115 #define wxPG_CAPRECTXMARGIN             2 // space between caption and selection rectangle,
116 #define wxPG_CAPRECTYMARGIN             1 // horizontally and vertically
117 
118 
119 #define PWC_CHILD_SUMMARY_LIMIT         16 // Maximum number of children summarized in a parent property's
120                                            // value field.
121 
122 #define PWC_CHILD_SUMMARY_CHAR_LIMIT    64 // Character limit of summary field when not editing
123 
124 #define wxPG_MIN_SCROLLBAR_WIDTH        10 // Smallest scrollbar width on any platform
125                                            // Must be larger than largest control border
126                                            // width * 2.
127 
128 
129 #define wxPG_DEFAULT_CURSOR             wxNullCursor
130 
131 
132 //
133 // Here are some extra platform dependent defines.
134 //
135 
136 #if defined(__WXMSW__)
137     // tested
138 
139     #define wxPG_DEFAULT_SPLITTERX      110 // default splitter position
140 
141     #define wxPG_NO_CHILD_EVT_MOTION    0 // 1 if splitter drag detect margin and control cannot overlap
142 
143     #define wxPG_CUSTOM_IMAGE_WIDTH     20 // for wxColourProperty etc.
144 
145     #define wxPG_ALLOW_EMPTY_TOOLTIPS   1  // If 1, then setting empty tooltip actually hides it
146 
147     #define wxPG_NAT_BUTTON_BORDER_ANY          1
148     #define wxPG_NAT_BUTTON_BORDER_X            1
149     #define wxPG_NAT_BUTTON_BORDER_Y            1
150 
151     #define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 0 // If 1 then controls are refreshed after selected was drawn.
152 
153 #elif defined(__WXGTK__)
154     // tested
155 
156     #define wxPG_DEFAULT_SPLITTERX      110
157 
158     #define wxPG_NO_CHILD_EVT_MOTION    1 // 1 if splitter drag detect margin and control cannot overlap
159 
160     #define wxPG_CUSTOM_IMAGE_WIDTH     20 // for wxColourProperty etc.
161 
162     #define wxPG_ALLOW_EMPTY_TOOLTIPS   0  // If 1, then setting empty tooltip actually hides it
163 
164     #define wxPG_NAT_BUTTON_BORDER_ANY      1
165     #define wxPG_NAT_BUTTON_BORDER_X        1
166     #define wxPG_NAT_BUTTON_BORDER_Y        1
167 
168     #define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 1 // If 1 then controls are refreshed after selected was drawn.
169 
170 #elif defined(__WXMAC__)
171     // *not* tested
172 
173     #define wxPG_DEFAULT_SPLITTERX      110
174 
175     #define wxPG_NO_CHILD_EVT_MOTION    0 // 1 if splitter drag detect margin and control cannot overlap
176 
177     #define wxPG_CUSTOM_IMAGE_WIDTH     20 // for wxColourProperty etc.
178 
179     #define wxPG_ALLOW_EMPTY_TOOLTIPS   1  // If 1, then setting empty tooltip actually hides it
180 
181     #define wxPG_NAT_BUTTON_BORDER_ANY      0
182     #define wxPG_NAT_BUTTON_BORDER_X        0
183     #define wxPG_NAT_BUTTON_BORDER_Y        0
184 
185     #define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 0 // If 1 then controls are refreshed after selected was drawn.
186 
187 #else
188     // defaults
189 
190     #define wxPG_DEFAULT_SPLITTERX      110
191 
192     #define wxPG_NO_CHILD_EVT_MOTION    1 // 1 if splitter drag detect margin and control cannot overlap
193 
194     #define wxPG_CUSTOM_IMAGE_WIDTH     20 // for wxColourProperty etc.
195 
196     #define wxPG_ALLOW_EMPTY_TOOLTIPS   0  // If 1, then setting empty tooltip actually hides it
197 
198     #define wxPG_NAT_BUTTON_BORDER_ANY      0
199     #define wxPG_NAT_BUTTON_BORDER_X        0
200     #define wxPG_NAT_BUTTON_BORDER_Y        0
201 
202     #define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 1 // If 1 then controls are refreshed after selected was drawn.
203 
204 #endif
205 
206 
207 #if wxPG_NO_CHILD_EVT_MOTION
208 
209     #define wxPG_SPLITTERX_DETECTMARGIN1    3 // this much on left
210     #define wxPG_SPLITTERX_DETECTMARGIN2    2 // this much on right
211 
212 #else
213 
214     #define wxPG_SPLITTERX_DETECTMARGIN1    3 // this much on left
215     #define wxPG_SPLITTERX_DETECTMARGIN2    2 // this much on right
216 
217 #endif
218 
219 
220 
221 //#define wxPG_NAT_CHOICE_BORDER_ANY   0
222 
223 #define wxPG_DRAG_MARGIN                30
224 
225 #define wxPG_CUSTOM_IMAGE_SPACINGY      1 // space between vertical sides of a custom image
226 
227 #define wxPG_HIDER_BUTTON_HEIGHT        25
228 
229 #define wxPG_PIXELS_PER_UNIT            m_lineHeight
230 
231 #define DEFAULT_IMAGE_OFFSET_INCREMENT \
232     (wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2)
233 
234 #ifdef wxPG_ICON_WIDTH
235   #define m_iconHeight m_iconWidth
236 #endif
237 
238 #define wxPG_TOOLTIP_DELAY              1000
239 
240 // This is the number of pixels the expander button inside
241 // property cells (i.e. not in the grey margin area are
242 // adjusted.
243 #define IN_CELL_EXPANDER_BUTTON_X_ADJUST    2
244 
245 #if wxMINOR_VERSION < 7 || ( wxMINOR_VERSION == 7 && wxRELEASE_NUMBER < 1 )
246 // NOTE: Since wxWidgets at this point doesn't have wxArrayDouble, we have
247 //   to create it ourself, using wxObjArray model.
248 #include <wx/arrimpl.cpp>
249 WX_DEFINE_OBJARRAY(wxArrayDouble);
250 #endif
251 
252 // -----------------------------------------------------------------------
253 
254 #if wxUSE_INTL
AutoGetTranslation(bool enable)255 void wxPropertyGrid::AutoGetTranslation ( bool enable )
256 {
257     wxPGGlobalVars->m_autoGetTranslation = enable;
258 }
259 #else
AutoGetTranslation(bool)260 void wxPropertyGrid::AutoGetTranslation ( bool ) { }
261 #endif
262 
263 // -----------------------------------------------------------------------
264 
wxPGIsWindowBuffered(const wxWindow * wnd)265 static bool wxPGIsWindowBuffered( const wxWindow* wnd )
266 {
267     return wnd->IsDoubleBuffered();
268 }
269 
270 // -----------------------------------------------------------------------
271 
272 const wxChar *wxPropertyGridNameStr = wxT("wxPropertyGrid");
273 
274 const wxChar *wxPGTypeName_long = wxT("long");
275 const wxChar *wxPGTypeName_bool = wxT("bool");
276 const wxChar *wxPGTypeName_double = wxT("double");
277 const wxChar *wxPGTypeName_wxString = wxT("string");
278 const wxChar *wxPGTypeName_void = wxT("void*");
279 const wxChar *wxPGTypeName_wxArrayString = wxT("arrstring");
280 
281 #ifdef __WXPYTHON__
282 const wxChar *wxPGTypeName_PyObject = wxT("PyObject");
283 #endif
284 
285 static const wxChar* gs_noCellText = wxT("@!");
286 
287 // -----------------------------------------------------------------------
288 
wxPGDrawFocusRect(wxDC & dc,const wxRect & rect)289 static void wxPGDrawFocusRect( wxDC& dc, const wxRect& rect )
290 {
291 #if defined(__WXMSW__) && !defined(__WXWINCE__)
292     // FIXME: Use DrawFocusRect code above (currently it draws solid line
293     //   for caption focus but works ok for other stuff).
294     //   Also, it seems that this code may not work in future wx versions.
295     dc.SetLogicalFunction(wxINVERT);
296 
297     wxPen pen(*wxBLACK,1,wxDOT);
298     pen.SetCap(wxCAP_BUTT);
299     dc.SetPen(pen);
300     dc.SetBrush(*wxTRANSPARENT_BRUSH);
301 
302     dc.DrawRectangle(rect);
303 
304     dc.SetLogicalFunction(wxCOPY);
305 #else
306     dc.SetLogicalFunction(wxINVERT);
307 
308     dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
309     dc.SetBrush(*wxTRANSPARENT_BRUSH);
310 
311     dc.DrawRectangle(rect);
312 
313     dc.SetLogicalFunction(wxCOPY);
314 #endif
315 }
316 
317 // -----------------------------------------------------------------------
318 
319 #if !wxCHECK_VERSION(3, 0, 0)
320 
321 //
322 // wxSplit (and maybe wxJoin) is needed for editable state code
323 //
324 
325 #include "wx/tokenzr.h"
326 
327 static
wxSplit(const wxString & str,const wxChar sep,const wxChar escape)328 wxArrayString wxSplit(const wxString& str, const wxChar sep, const wxChar escape)
329 {
330     if ( escape == wxT('\0') )
331     {
332         // simple case: we don't need to honour the escape character
333         return wxStringTokenize(str, sep, wxTOKEN_RET_EMPTY_ALL);
334     }
335 
336     wxArrayString ret;
337     wxString curr;
338     wxChar prev = wxT('\0');
339 
340     for ( wxString::const_iterator i = str.begin(),
341                                  end = str.end();
342           i != end;
343           ++i )
344     {
345         const wxChar ch = *i;
346 
347         if ( ch == sep )
348         {
349             if ( prev == escape )
350             {
351                 // remove the escape character and don't consider this
352                 // occurrence of 'sep' as a real separator
353                 *curr.rbegin() = sep;
354             }
355             else // real separator
356             {
357                 ret.push_back(curr);
358                 curr.clear();
359             }
360         }
361         else // normal character
362         {
363             curr += ch;
364         }
365 
366         prev = ch;
367     }
368 
369     // add the last token
370     if ( !curr.empty() || prev == sep )
371         ret.Add(curr);
372 
373     return ret;
374 }
375 
376 #endif  // !wxCHECK_VERSION(3, 0, 0)
377 
378 // -----------------------------------------------------------------------
379 // Choice related methods from various classes
380 // -----------------------------------------------------------------------
381 
AddPropertyChoice(wxPGPropArg id,const wxString & label,int value)382 void wxPropertyGridInterface::AddPropertyChoice( wxPGPropArg id,
383                                                  const wxString& label,
384                                                  int value )
385 {
386     wxPG_PROP_ARG_CALL_PROLOG()
387 
388     p->InsertChoice(label,-1,value);
389 }
390 
391 
InsertPropertyChoice(wxPGPropArg id,const wxString & label,int index,int value)392 void wxPropertyGridInterface::InsertPropertyChoice( wxPGPropArg id,
393                                                     const wxString& label,
394                                                     int index,
395                                                     int value )
396 {
397     wxPG_PROP_ARG_CALL_PROLOG()
398 
399     p->InsertChoice(label,index,value);
400 }
401 
402 
DeletePropertyChoice(wxPGPropArg id,int index)403 void wxPropertyGridInterface::DeletePropertyChoice( wxPGPropArg id,
404                                                     int index )
405 {
406     wxPG_PROP_ARG_CALL_PROLOG()
407 
408     p->DeleteChoice(index);
409 }
410 
411 
412 // -----------------------------------------------------------------------
413 // Statics in one class for easy destruction.
414 // NB: We prefer to use wxModule, as it offers more consistent behavior
415 //     across platforms. However, for those rare problem situations, we
416 //     also need to offer option to use simpler approach.
417 // -----------------------------------------------------------------------
418 
419 #define wxPG_USE_WXMODULE 0
420 
421 #ifndef wxPG_USE_WXMODULE
422     #define wxPG_USE_WXMODULE 1
423 #endif
424 
425 #if wxPG_USE_WXMODULE
426 
427 #include <wx/module.h>
428 
429 class wxPGGlobalVarsClassManager : public wxModule
430 {
431     DECLARE_DYNAMIC_CLASS(wxPGGlobalVarsClassManager)
432 public:
wxPGGlobalVarsClassManager()433     wxPGGlobalVarsClassManager() {}
OnInit()434     virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; }
OnExit()435     virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; }
436 };
437 
IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager,wxModule)438 IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
439 
440 
441 // When wxPG is loaded dynamically after the application is already running
442 // then the built-in module system won't pick this one up.  Add it manually.
443 void wxPGInitResourceModule()
444 {
445     wxModule* module = new wxPGGlobalVarsClassManager;
446     module->Init();
447     wxModule::RegisterModule(module);
448 }
449 
450 
451 #else // !wxPG_USE_WXMODULE
452 
453 class wxPGGlobalVarsClassManager
454 {
455 public:
wxPGGlobalVarsClassManager()456     wxPGGlobalVarsClassManager() {}
~wxPGGlobalVarsClassManager()457     ~wxPGGlobalVarsClassManager() { delete wxPGGlobalVars; }
458 };
459 
460 static wxPGGlobalVarsClassManager gs_pgGlobalVarsClassManager;
461 
462 #endif
463 
464 
465 wxPGGlobalVarsClass* wxPGGlobalVars = (wxPGGlobalVarsClass*) NULL;
466 
467 
wxPGGlobalVarsClass()468 wxPGGlobalVarsClass::wxPGGlobalVarsClass()
469 {
470     m_boolChoices.Add(_("False"));
471     m_boolChoices.Add(_("True"));
472 
473     m_fontFamilyChoices = (wxPGChoices*) NULL;
474 
475     m_defaultRenderer = new wxPGDefaultRenderer();
476 
477     m_autoGetTranslation = false;
478 
479     m_offline = 0;
480 
481     m_extraStyle = 0;
482 
483     wxVariant v;
484 
485     v = (long)0;
486     wxVariantClassInfo_long = wxPGVariantDataGetClassInfo(v.GetData());
487 
488     v = wxString();
489     wxVariantClassInfo_string = wxPGVariantDataGetClassInfo(v.GetData());
490 
491     v = (double)0.0;
492     wxVariantClassInfo_double = wxPGVariantDataGetClassInfo(v.GetData());
493 
494     v = (bool)false;
495     wxVariantClassInfo_bool = wxPGVariantDataGetClassInfo(v.GetData());
496 
497     v = wxArrayString();
498     wxVariantClassInfo_arrstring = wxPGVariantDataGetClassInfo(v.GetData());
499 
500     wxColour col;
501     wxVariant v2((wxObject*)&col);
502     wxVariantClassInfo_wxobject = wxPGVariantDataGetClassInfo(v2.GetData());
503 
504     wxVariantList list;
505     v = wxVariant(list);
506     wxVariantClassInfo_list = wxPGVariantDataGetClassInfo(v.GetData());
507 
508     v << *wxRED;
509     wxVariantClassInfo_wxColour = wxPGVariantDataGetClassInfo(v.GetData());
510 
511 #if wxUSE_DATETIME
512     v = wxVariant(wxDateTime::Now());
513     wxVariantClassInfo_datetime = wxPGVariantDataGetClassInfo(v.GetData());
514 #endif
515 
516 	// Prepare some shared variants
517     m_vEmptyString = wxString();
518     m_vZero = (long) 0;
519     m_vMinusOne = (long) -1;
520     m_vTrue = true;
521     m_vFalse = false;
522 
523     // Prepare cached string constants
524     m_strDefaultValue = wxT("DefaultValue");
525     m_strMin = wxT("Min");
526     m_strMax = wxT("Max");
527     m_strUnits = wxT("Units");
528     m_strInlineHelp = wxT("InlineHelp");
529 
530 #ifdef __WXDEBUG__
531     m_warnings = 0;
532 #endif
533 }
534 
535 
~wxPGGlobalVarsClass()536 wxPGGlobalVarsClass::~wxPGGlobalVarsClass()
537 {
538     size_t i;
539 
540     delete m_defaultRenderer;
541 
542     // This will always have one ref
543     delete m_fontFamilyChoices;
544 
545 #if wxUSE_VALIDATORS
546     for ( i=0; i<m_arrValidators.GetCount(); i++ )
547         delete ((wxValidator*)m_arrValidators[i]);
548 #endif
549 
550     //
551     // Destroy value type class instances.
552     wxPGHashMapS2P::iterator vt_it;
553 
554     // Destroy editor class instances.
555     // iterate over all the elements in the class
556     for( vt_it = m_mapEditorClasses.begin(); vt_it != m_mapEditorClasses.end(); ++vt_it )
557     {
558         wxPGEditor* editor = (wxPGEditor*) vt_it->second;
559         delete editor;
560     }
561 }
562 
563 // -----------------------------------------------------------------------
564 // wxPGProperty
565 // -----------------------------------------------------------------------
566 
567 IMPLEMENT_ABSTRACT_CLASS(wxPGProperty, wxObject)
568 
569 wxString wxString_wxPG_LABEL = wxT("_LABEL_AS_NAME");
570 
Init()571 void wxPGProperty::Init()
572 {
573 #ifdef __WXPYTHON__
574     m_scriptObject = NULL;
575     m_OORClientData = NULL;
576 #endif
577 
578     m_commonValue = -1;
579     m_arrIndex = 0xFFFF;
580     m_parent = NULL;
581 
582     m_parentState = (wxPropertyGridState*) NULL;
583 
584     m_clientData = NULL;
585 
586     m_customEditor = (wxPGEditor*) NULL;
587 #if wxUSE_VALIDATORS
588     m_validator = (wxValidator*) NULL;
589 #endif
590     m_valueBitmap = (wxBitmap*) NULL;
591 
592     m_maxLen = 0; // infinite maximum length
593 
594     m_flags = wxPG_PROP_PROPERTY;
595 
596     m_depth = 1;
597     m_bgColIndex = 0;
598     m_fgColIndex = 0;
599 
600     SetExpanded(true);
601 }
602 
603 
Init(const wxString & label,const wxString & name)604 void wxPGProperty::Init( const wxString& label, const wxString& name )
605 {
606     m_label = label;
607 
608 #ifndef __WXPYTHON__
609     if ( name != wxString_wxPG_LABEL)
610 #else
611     if ( (&name != ((wxString*)NULL)) && name != wxT("_LABEL_AS_NAME") )
612 #endif
613         DoSetName( name );
614     else
615         DoSetName( m_label );
616 
617     Init();
618 }
619 
wxPGProperty()620 wxPGProperty::wxPGProperty()
621     : wxObject()
622 {
623     Init();
624 }
625 
626 
wxPGProperty(const wxString & label,const wxString & name)627 wxPGProperty::wxPGProperty( const wxString& label, const wxString& name )
628     : wxObject()
629 {
630     Init( label, name );
631 }
632 
633 
~wxPGProperty()634 wxPGProperty::~wxPGProperty()
635 {
636 #ifdef __WXPYTHON__
637     delete m_OORClientData;
638     if ( m_clientData )
639         Py_DECREF( m_clientData );
640 #endif
641 
642     DoEmpty();  // this deletes items
643 
644     delete m_valueBitmap;
645 #if wxUSE_VALIDATORS
646     delete m_validator;
647 #endif
648 
649     unsigned int i;
650 
651     for ( i=0; i<m_cells.size(); i++ )
652         delete (wxPGCell*) m_cells[i];
653 
654     // This makes it easier for us to detect dangling pointers
655     m_parent = NULL;
656 }
657 
658 
IsSomeParent(wxPGProperty * candidate) const659 bool wxPGProperty::IsSomeParent( wxPGProperty* candidate ) const
660 {
661     wxPGProperty* parent = m_parent;
662     do
663     {
664         if ( parent == candidate )
665             return true;
666         parent = parent->m_parent;
667     } while ( parent );
668     return false;
669 }
670 
SetName(const wxString & newName)671 void wxPGProperty::SetName( const wxString& newName )
672 {
673     wxPropertyGrid* pg = GetGrid();
674 
675     if ( pg )
676         pg->SetPropertyName(this, newName);
677     else
678         DoSetName(newName);
679 }
680 
GetName() const681 wxString wxPGProperty::GetName() const
682 {
683     wxPGProperty* parent = GetParent();
684 
685     if ( !m_name.length() || !parent || parent->IsCategory() || parent->IsRoot() )
686         return m_name;
687 
688     return m_parent->GetName() + wxT(".") + m_name;
689 }
690 
GetGrid() const691 wxPropertyGrid* wxPGProperty::GetGrid() const
692 {
693     if ( !m_parentState )
694         return NULL;
695     return m_parentState->GetGrid();
696 }
697 
ValidateValue(wxVariant & WXUNUSED (value),wxPGValidationInfo & WXUNUSED (validationInfo)) const698 bool wxPGProperty::ValidateValue( wxVariant& WXUNUSED(value), wxPGValidationInfo& WXUNUSED(validationInfo) ) const
699 {
700     return true;
701 }
702 
OnSetValue()703 void wxPGProperty::OnSetValue()
704 {
705 }
706 
RefreshChildren()707 void wxPGProperty::RefreshChildren ()
708 {
709 }
710 
OnValidationFailure(wxVariant & WXUNUSED (pendingValue))711 void wxPGProperty::OnValidationFailure( wxVariant& WXUNUSED(pendingValue) )
712 {
713 }
714 
GetColumnText(unsigned int col) const715 wxString wxPGProperty::GetColumnText( unsigned int col ) const
716 {
717     wxPGCell* cell = GetCell(col);
718     if ( cell )
719     {
720         return cell->GetText();
721     }
722     else
723     {
724         if ( col == 0 )
725             return GetLabel();
726         else if ( col == 1 )
727             return GetDisplayedString();
728         else if ( col == 2 )
729             return GetAttribute(wxPGGlobalVars->m_strUnits, wxEmptyString);
730     }
731 
732     return wxEmptyString;
733 }
734 
GenerateComposedValue(wxString & text,int argFlags) const735 void wxPGProperty::GenerateComposedValue( wxString& text, int argFlags ) const
736 {
737     int i;
738     int iMax = m_children.GetCount();
739 
740     text.clear();
741     if ( iMax == 0 )
742         return;
743 
744     if ( iMax > PWC_CHILD_SUMMARY_LIMIT &&
745          !(argFlags & wxPG_FULL_VALUE) )
746         iMax = PWC_CHILD_SUMMARY_LIMIT;
747 
748     int iMaxMinusOne = iMax-1;
749 
750     if ( !IsTextEditable() )
751         argFlags |= wxPG_UNEDITABLE_COMPOSITE_FRAGMENT;
752 
753     wxPGProperty* curChild = (wxPGProperty*) m_children.Item(0);
754 
755     for ( i = 0; i < iMax; i++ )
756     {
757         wxString s;
758         if ( !curChild->IsValueUnspecified() )
759             s = curChild->GetValueString(argFlags|wxPG_COMPOSITE_FRAGMENT);
760 
761         bool skip = false;
762         if ( (argFlags & wxPG_UNEDITABLE_COMPOSITE_FRAGMENT) && !s.length() )
763             skip = true;
764 
765         if ( !curChild->GetChildCount() || skip )
766             text += s;
767         else
768             text += wxT("[") + s + wxT("]");
769 
770         if ( i < iMaxMinusOne )
771         {
772             if ( text.length() > PWC_CHILD_SUMMARY_CHAR_LIMIT &&
773                  !(argFlags & wxPG_EDITABLE_VALUE) &&
774                  !(argFlags & wxPG_FULL_VALUE) )
775                 break;
776 
777             if ( !skip )
778             {
779                 if ( !curChild->GetChildCount() )
780                     text += wxT("; ");
781                 else
782                     text += wxT(" ");
783             }
784 
785             curChild = (wxPGProperty*) m_children.Item(i+1);
786         }
787     }
788 
789     if ( (unsigned int)i < m_children.GetCount() )
790     {
791         if ( !wxPG_String_EndsWith(text, wxT("; ")) )
792             text += wxT("; ...");
793         else
794             text += wxT("...");
795     }
796 }
797 
GetValueAsString(int argFlags) const798 wxString wxPGProperty::GetValueAsString( int argFlags ) const
799 {
800     wxCHECK_MSG( GetCount() > 0,
801                  wxString(),
802                  wxT("If user property does not have any children, it must override GetValueAsString") );
803 
804     wxString text;
805     GenerateComposedValue(text, argFlags);
806     return text;
807 }
808 
GetValueString(int argFlags) const809 wxString wxPGProperty::GetValueString( int argFlags ) const
810 {
811     wxPropertyGrid* pg = GetGrid();
812 
813     if ( IsValueUnspecified() )
814         return pg->GetUnspecifiedValueText(argFlags);
815 
816     if ( m_commonValue == -1 )
817         return GetValueAsString(argFlags);
818 
819     //
820     // Return common value's string representation
821     const wxPGCommonValue* cv = pg->GetCommonValue(m_commonValue);
822 
823     if ( argFlags & wxPG_FULL_VALUE )
824     {
825         return cv->GetLabel();
826     }
827     else if ( argFlags & wxPG_EDITABLE_VALUE )
828     {
829         return cv->GetEditableText();
830     }
831     else
832     {
833         return cv->GetLabel();
834     }
835 }
836 
IntToValue(wxVariant & variant,int number,int WXUNUSED (argFlags)) const837 bool wxPGProperty::IntToValue( wxVariant& variant, int number, int WXUNUSED(argFlags) ) const
838 {
839     variant = (long)number;
840     return true;
841 }
842 
843 // Convert semicolon delimited tokens into child values.
StringToValue(wxVariant & variant,const wxString & text,int argFlags) const844 bool wxPGProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
845 {
846     if ( !GetCount() )
847         return false;
848 
849     unsigned int curChild = 0;
850 
851     unsigned int iMax = m_children.GetCount();
852 
853     if ( iMax > PWC_CHILD_SUMMARY_LIMIT &&
854          !(argFlags & wxPG_FULL_VALUE) )
855         iMax = PWC_CHILD_SUMMARY_LIMIT;
856 
857     bool changed = false;
858 
859     wxString token;
860     size_t pos = 0;
861 
862     // Its best only to add non-empty group items
863     bool addOnlyIfNotEmpty = false;
864     const wxChar delimeter = wxT(';');
865 
866     size_t tokenStart = 0xFFFFFF;
867 
868     wxVariantList temp_list;
869     wxVariant list(temp_list);
870 
871     int propagatedFlags = argFlags & (wxPG_REPORT_ERROR|wxPG_PROGRAMMATIC_VALUE);
872 
873 #ifdef __WXDEBUG__
874     bool debug_print = false;
875 #endif
876 
877 #ifdef __WXDEBUG__
878     if ( debug_print )
879         wxLogDebug(wxT(">> %s.StringToValue('%s')"),GetLabel().c_str(),text.c_str());
880 #endif
881 
882     wxString::const_iterator it = text.begin();
883     wxUniChar a;
884 
885     if ( it != text.end() )
886         a = *it;
887     else
888         a = 0;
889 
890     for ( ;; )
891     {
892         // How many units we iterate string forward at the end of loop?
893         // We need to keep track of this or risk going to negative
894         // with it-- operation.
895         unsigned int strPosIncrement = 1;
896 
897         if ( tokenStart != 0xFFFFFF )
898         {
899             // Token is running
900             if ( a == delimeter || a == 0 )
901             {
902                 token = text.substr(tokenStart,pos-tokenStart);
903                 token.Trim(true);
904                 size_t len = token.length();
905 
906                 if ( !addOnlyIfNotEmpty || len > 0 )
907                 {
908                     const wxPGProperty* child = Item(curChild);
909                     wxVariant variant(child->GetValue());
910                     wxString childName(child->GetBaseName());
911 
912                 #ifdef __WXDEBUG__
913                     if ( debug_print )
914                         wxLogDebug(wxT("token = '%s', child = %s"),token.c_str(),childName.c_str());
915                 #endif
916 
917                     // Add only if editable or setting programmatically
918                     if ( (argFlags & wxPG_PROGRAMMATIC_VALUE) ||
919                          !child->HasFlag(wxPG_PROP_DISABLED|wxPG_PROP_READONLY) )
920                     {
921                         if ( len > 0 )
922                         {
923                             if ( child->ActualStringToValue(variant, token,
924                                  propagatedFlags|wxPG_COMPOSITE_FRAGMENT) )
925                             {
926                                 // We really need to set the variant's name
927                                 // *after* child->StringToValue() has been
928                                 // called, since variant's value may be set by
929                                 // assigning another variant into it, which
930                                 // then usually causes name to be copied (ie.
931                                 // usually cleared) as well. wxBoolProperty
932                                 // being case in point with its use of
933                                 // wxPGVariant_Bool macro as an optimization.
934                                 variant.SetName(childName);
935                                 list.Append(variant);
936 
937                                 changed = true;
938                             }
939                         }
940                         else
941                         {
942                             // Empty, becomes unspecified
943                             variant.MakeNull();
944                             variant.SetName(childName);
945                             list.Append(variant);
946                             changed = true;
947                         }
948                     }
949 
950                     curChild++;
951                     if ( curChild >= iMax )
952                         break;
953                 }
954 
955                 tokenStart = 0xFFFFFF;
956             }
957         }
958         else
959         {
960             // Token is not running
961             if ( a != wxT(' ') )
962             {
963 
964                 addOnlyIfNotEmpty = false;
965 
966                 // Is this a group of tokens?
967                 if ( a == wxT('[') )
968                 {
969                     int depth = 1;
970 
971                     if ( it != text.end() ) it++;
972                     pos++;
973                     size_t startPos = pos;
974 
975                     // Group item - find end
976                     while ( it != text.end() && depth > 0 )
977                     {
978                         a = *it;
979                         it++;
980                         pos++;
981 
982                         if ( a == wxT(']') )
983                             depth--;
984                         else if ( a == wxT('[') )
985                             depth++;
986                     }
987 
988                     token = text.substr(startPos,pos-startPos-1);
989 
990                     if ( !token.length() )
991                         break;
992 
993                     const wxPGProperty* child = Item(curChild);
994 
995                     // Add only if editable or setting programmatically
996                     if ( (argFlags & wxPG_PROGRAMMATIC_VALUE) ||
997                          !child->HasFlag(wxPG_PROP_DISABLED|wxPG_PROP_READONLY) )
998                     {
999                         wxVariant variant(child->GetValue());
1000                         if ( child->ActualStringToValue( variant, token, propagatedFlags ) )
1001                         {
1002                             variant.SetName(child->GetBaseName());
1003                             list.Append(variant);
1004                             changed = true;
1005                         }
1006                         else
1007                         {
1008                             // Failed, becomes unspecified
1009                             variant.MakeNull();
1010                             list.Append(variant);
1011                             changed = true;
1012                         }
1013                     }
1014 
1015                     curChild++;
1016                     if ( curChild >= iMax )
1017                         break;
1018 
1019                     addOnlyIfNotEmpty = true;
1020 
1021                     tokenStart = 0xFFFFFF;
1022                 }
1023                 else
1024                 {
1025                     tokenStart = pos;
1026 
1027                     if ( a == delimeter )
1028                         strPosIncrement--;
1029                 }
1030             }
1031         }
1032 
1033         if ( a == 0 )
1034             break;
1035 
1036         it += strPosIncrement;
1037         if ( it != text.end() )
1038         {
1039             a = *it;
1040         }
1041         else
1042         {
1043             a = 0;
1044         }
1045         pos += strPosIncrement;
1046     }
1047 
1048     if ( changed )
1049         variant = list;
1050 
1051     return changed;
1052 }
1053 
1054 #ifdef __WXPYTHON__
PyValidateValue(const wxVariant & value,wxPGValidationInfo & validationInfo) const1055 wxPGVariantAndBool wxPGProperty::PyValidateValue( const wxVariant& value, wxPGValidationInfo& validationInfo ) const
1056 {
1057     wxPGVariantAndBool vab;
1058     vab.m_value = value;
1059     vab.m_valueValid = true;
1060     vab.m_result = ValidateValue(vab.m_value, validationInfo);
1061     return vab;
1062 }
1063 
PyStringToValue(const wxString & text,int argFlags) const1064 wxPGVariantAndBool wxPGProperty::PyStringToValue( const wxString& text, int argFlags ) const
1065 {
1066     wxPGVariantAndBool vab;
1067     vab.m_result = StringToValue(vab.m_value, text, argFlags);
1068     if ( vab.m_result )
1069         vab.m_valueValid = true;
1070     return vab;
1071 }
1072 
PyIntToValue(int number,int argFlags) const1073 wxPGVariantAndBool wxPGProperty::PyIntToValue( int number, int argFlags ) const
1074 {
1075     wxPGVariantAndBool vab;
1076     vab.m_result = IntToValue(vab.m_value, number, argFlags);
1077     if ( vab.m_result )
1078         vab.m_valueValid = true;
1079     return vab;
1080 }
1081 
1082 wxVariant
PyChildChanged(wxVariant & thisValue,int WXUNUSED (childIndex),wxVariant & WXUNUSED (childValue)) const1083 wxPGProperty::PyChildChanged( wxVariant& thisValue,
1084                               int WXUNUSED(childIndex),
1085                               wxVariant& WXUNUSED(childValue) ) const
1086 {
1087     return thisValue;
1088 }
1089 
1090 #endif
1091 
SetValueFromString(const wxString & text,int argFlags)1092 bool wxPGProperty::SetValueFromString( const wxString& text, int argFlags )
1093 {
1094 #if wxPG_COMPATIBILITY_1_2_0
1095     if ( argFlags == 0xFFFF )
1096     {
1097         // Do not override! (for backwards compliancy)
1098         m_commonValue = -1234;
1099         return true;
1100     }
1101 #endif
1102 
1103     wxVariant variant(m_value);
1104     bool res = ActualStringToValue(variant, text, argFlags);
1105     if ( res )
1106         SetValue(variant);
1107     return res;
1108 }
1109 
SetValueFromInt(long number,int argFlags)1110 bool wxPGProperty::SetValueFromInt( long number, int argFlags )
1111 {
1112 #if wxPG_COMPATIBILITY_1_2_0
1113     if ( argFlags == 0xFFFF )
1114     {
1115         // Do not override! (for backwards compliancy)
1116         m_commonValue = -1234;
1117         return true;
1118     }
1119 #endif
1120 
1121     wxVariant variant(m_value);
1122     bool res = ActualIntToValue(variant, number, argFlags);
1123     if ( res )
1124         SetValue(variant);
1125     return res;
1126 }
1127 
OnMeasureImage(int WXUNUSED (item)) const1128 wxSize wxPGProperty::OnMeasureImage( int WXUNUSED(item) ) const
1129 {
1130     if ( m_valueBitmap )
1131         return wxSize(m_valueBitmap->GetWidth(),-1);
1132 
1133     return wxSize(0,0);
1134 }
1135 
1136 #if wxPG_COMPATIBILITY_1_2_0
GetImageSize() const1137 wxSize wxPGProperty::GetImageSize() const
1138 {
1139     return wxSize(-1234,-1234);
1140 }
1141 #endif
1142 
GetImageOffset(int imageWidth) const1143 int wxPGProperty::GetImageOffset( int imageWidth ) const
1144 {
1145     int imageOffset = 0;
1146 
1147     if ( imageWidth )
1148     {
1149         // Do not increment offset too much for wide images
1150         if ( imageWidth <= (wxPG_CUSTOM_IMAGE_WIDTH+5) )
1151             imageOffset = imageWidth + DEFAULT_IMAGE_OFFSET_INCREMENT;
1152         else
1153             imageOffset = imageWidth + 1;
1154     }
1155 
1156     return imageOffset;
1157 }
1158 
GetCellRenderer(int WXUNUSED (column)) const1159 wxPGCellRenderer* wxPGProperty::GetCellRenderer( int WXUNUSED(column) ) const
1160 {
1161     return wxPGGlobalVars->m_defaultRenderer;
1162 }
1163 
1164 
OnCustomPaint(wxDC & dc,const wxRect & rect,wxPGPaintData &)1165 void wxPGProperty::OnCustomPaint( wxDC& dc,
1166                                   const wxRect& rect,
1167                                   wxPGPaintData& )
1168 {
1169     wxBitmap* bmp = m_valueBitmap;
1170 
1171     wxCHECK_RET( bmp && bmp->Ok(), wxT("invalid bitmap") );
1172 
1173     wxCHECK_RET( rect.x >= 0, wxT("unexpected measure call") );
1174 
1175     dc.DrawBitmap(*bmp,rect.x,rect.y);
1176 }
1177 
DoGetEditorClass() const1178 const wxPGEditor* wxPGProperty::DoGetEditorClass() const
1179 {
1180     return wxPG_EDITOR(TextCtrl);
1181 }
1182 
1183 #ifdef __WXPYTHON__
GetEditor() const1184 wxString wxPGProperty::GetEditor() const
1185 {
1186     return wxEmptyString;
1187 }
1188 #endif
1189 
1190 
1191 // Default extra property event handling - that is, none at all.
OnEvent(wxPropertyGrid *,wxWindow *,wxEvent &)1192 bool wxPGProperty::OnEvent( wxPropertyGrid*, wxWindow*, wxEvent& )
1193 {
1194     return false;
1195 }
1196 
1197 
SetValue(wxVariant value,wxVariant * pList,int flags)1198 void wxPGProperty::SetValue( wxVariant value, wxVariant* pList, int flags )
1199 {
1200 #ifdef __WXPYTHON__
1201     // Translate Py_None to Null wxVariant
1202     PyObject* obj = PyObjectFromVariant(value);
1203     if ( obj )
1204     {
1205         if ( obj == Py_None )
1206             value.MakeNull();
1207         Py_DECREF(obj);
1208     }
1209 #endif
1210 
1211     // If auto unspecified values are not wanted (via window or property style),
1212     // then get default value instead of wxNullVariant.
1213     if ( value.IsNull() && (flags & wxPG_SETVAL_BY_USER) &&
1214          !UsesAutoUnspecified() )
1215     {
1216         value = GetDefaultValue();
1217     }
1218 
1219     if ( !value.IsNull() )
1220     {
1221         wxVariant tempListVariant;
1222 
1223         SetCommonValue(-1);
1224 
1225         // List variants are reserved a special purpose
1226         // as intermediate containers for child values
1227         // of properties with children.
1228         if ( wxPGIsVariantType(value, list) )
1229         {
1230             //
1231             // However, situation is different for composed string properties
1232             if ( HasFlag(wxPG_PROP_COMPOSED_VALUE) )
1233             {
1234                 tempListVariant = value;
1235                 pList = &tempListVariant;
1236             }
1237 
1238             wxVariant newValue;
1239             AdaptListToValue(value, &newValue);
1240             value = newValue;
1241             //wxLogDebug(wxT(">> %s.SetValue() adapted list value to type '%s'"),GetName().c_str(),value.GetType().c_str());
1242         }
1243 
1244         if ( HasFlag( wxPG_PROP_AGGREGATE) )
1245             flags |= wxPG_SETVAL_AGGREGATED;
1246 
1247         if ( pList && !pList->IsNull() )
1248         {
1249             wxASSERT( wxPGIsVariantType(*pList, list) );
1250             wxASSERT( GetChildCount() );
1251             wxASSERT( !IsCategory() );
1252 
1253             wxVariantList& list = pList->GetList();
1254             wxVariantList::iterator node;
1255             unsigned int i = 0;
1256 
1257             //wxLogDebug(wxT(">> %s.SetValue() pList parsing"),GetName().c_str());
1258 
1259             // Children in list can be in any order, but we will give hint to
1260             // GetPropertyByNameWH(). This optimizes for full list parsing.
1261             for ( node = list.begin(); node != list.end(); node++ )
1262             {
1263                 wxVariant& childValue = *((wxVariant*)*node);
1264                 wxPGProperty* child = GetPropertyByNameWH(childValue.GetName(), i);
1265                 if ( child )
1266                 {
1267                     //wxLogDebug(wxT("%i: child = %s, childValue.GetType()=%s, child.GetValue().GetType()=%s"),i,child->GetBaseName().c_str(),childValue.GetType().c_str(),child->GetValue().GetType().c_str());
1268                     if ( wxPGIsVariantType(childValue, list) )
1269                     {
1270                         if ( child->HasFlag(wxPG_PROP_AGGREGATE) && !(flags & wxPG_SETVAL_AGGREGATED) )
1271                         {
1272                             wxVariant listRefCopy = childValue;
1273                             child->SetValue(childValue, &listRefCopy, flags|wxPG_SETVAL_FROM_PARENT);
1274                         }
1275                         else
1276                         {
1277                             wxVariant oldVal = child->GetValue();
1278                             child->SetValue(oldVal, &childValue, flags|wxPG_SETVAL_FROM_PARENT);
1279                         }
1280                     }
1281                     else if ( !wxPG_VARIANT_EQ(child->GetValue(), childValue) )
1282                     {
1283                         // For aggregate properties, we will trust RefreshChildren()
1284                         // to update child values.
1285                         if ( !IsFlagSet(wxPG_PROP_AGGREGATE) )
1286                             child->SetValue(childValue, NULL, flags|wxPG_SETVAL_FROM_PARENT);
1287                         if ( flags & wxPG_SETVAL_BY_USER )
1288                             child->SetFlag(wxPG_PROP_MODIFIED);
1289                     }
1290                 }
1291                 i++;
1292             }
1293         }
1294 
1295         if ( !value.IsNull() )
1296         {
1297             wxPGVariantAssign(m_value, value);
1298             OnSetValue();
1299         }
1300 
1301         if ( flags & wxPG_SETVAL_BY_USER )
1302             SetFlag(wxPG_PROP_MODIFIED);
1303 
1304         if ( IsFlagSet(wxPG_PROP_AGGREGATE) )
1305             RefreshChildren();
1306     }
1307     else
1308     {
1309         if ( m_commonValue != -1 )
1310         {
1311             wxPropertyGrid* pg = GetGrid();
1312             if ( !pg || m_commonValue != pg->GetUnspecifiedCommonValue() )
1313                 SetCommonValue(-1);
1314         }
1315 
1316         m_value = value;
1317 
1318         // Set children to unspecified, but only if aggregate or
1319         // value is <composed>
1320         if ( AreChildrenComponents() )
1321         {
1322             unsigned int i;
1323             for ( i=0; i<GetChildCount(); i++ )
1324                 Item(i)->SetValue(value, NULL, flags|wxPG_SETVAL_FROM_PARENT);
1325         }
1326     }
1327 
1328     if ( !(flags & wxPG_SETVAL_FROM_PARENT) )
1329         UpdateParentValues();
1330 
1331     //
1332     // Update editor control.
1333     if ( flags & wxPG_SETVAL_REFRESH_EDITOR )
1334     {
1335         wxPropertyGrid* pg = GetGridIfDisplayed();
1336         if ( pg )
1337         {
1338             wxPGProperty* selected = pg->GetSelectedProperty();
1339 
1340             // Only refresh the control if this was selected, or
1341             // this was some parent of selected, or vice versa)
1342             if ( selected && (selected == this ||
1343                               selected->IsSomeParent(this) ||
1344                               this->IsSomeParent(selected)) )
1345                 RefreshEditor();
1346 
1347             pg->DrawItemAndValueRelated(this);
1348         }
1349     }
1350 }
1351 
1352 
SetValueInEvent(wxVariant value) const1353 void wxPGProperty::SetValueInEvent( wxVariant value ) const
1354 {
1355     GetGrid()->ValueChangeInEvent(value);
1356 }
1357 
SetFlagRecursively(FlagType flag,bool set)1358 void wxPGProperty::SetFlagRecursively( FlagType flag, bool set )
1359 {
1360     ChangeFlag(flag, set);
1361 
1362     unsigned int i;
1363     for ( i = 0; i < GetChildCount(); i++ )
1364         Item(i)->SetFlagRecursively(flag, set);
1365 }
1366 
RefreshEditor()1367 void wxPGProperty::RefreshEditor()
1368 {
1369     if ( !m_parent )
1370         return;
1371 
1372     wxPropertyGrid* pg = GetGrid();
1373     if ( pg && pg->GetSelectedProperty() == this )
1374         pg->RefreshEditor();
1375 }
1376 
GetDefaultValue() const1377 wxVariant wxPGProperty::GetDefaultValue() const
1378 {
1379     wxVariant defVal = GetAttribute(wxPG_ATTR_DEFAULT_VALUE);
1380     if ( !defVal.IsNull() )
1381         return defVal;
1382 
1383     wxVariant value = GetValue();
1384 
1385     if ( !value.IsNull() )
1386     {
1387         wxPGVariantDataClassInfo classInfo = wxPGVariantDataGetClassInfo(value.GetData());
1388         if ( wxPGIsVariantClassInfo(classInfo, long) )
1389             return wxPGVariant_Zero;
1390         if ( wxPGIsVariantClassInfo(classInfo, string) )
1391             return wxPGVariant_EmptyString;
1392         if ( wxPGIsVariantClassInfo(classInfo, bool) )
1393             return wxPGVariant_False;
1394         if ( wxPGIsVariantClassInfo(classInfo, double) )
1395             return wxVariant(0.0);
1396 
1397         wxPGVariantData* pgvdata = wxDynamicCastVariantData(m_value.GetData(), wxPGVariantData);
1398         if ( pgvdata )
1399             return pgvdata->GetDefaultValue();
1400 
1401         if ( wxPGIsVariantClassInfo(classInfo, arrstring) )
1402             return wxVariant(wxArrayString());
1403         if ( wxPGIsVariantClassInfo(classInfo, wxColour) )
1404             return WXVARIANT(*wxRED);
1405 #if wxUSE_DATETIME
1406         if ( wxPGIsVariantClassInfo(classInfo, datetime) )
1407             return wxVariant(wxDateTime::Now());
1408 #endif
1409 
1410         wxFAIL_MSG(
1411             wxString::Format(wxT("Inorder for value to have default value, it must be added to")
1412                              wxT("wxPGProperty::GetDefaultValue or it's variantdata must inherit")
1413                              wxT("from wxPGVariantData (unrecognized type was '%s')"),m_value.GetType().c_str())
1414                   );
1415     }
1416 
1417     return wxVariant();
1418 }
1419 
InsertChild(int index,wxPGProperty * childProperty)1420 wxPGProperty* wxPGProperty::InsertChild( int index,
1421                                          wxPGProperty* childProperty )
1422 {
1423     if ( index < 0 )
1424         index = m_children.size();
1425 
1426     wxASSERT_MSG( m_parentState,
1427                   wxT("Add property to a grid or page before ")
1428                   wxT("adding children.") );
1429 
1430     m_parentState->DoInsert(this, index, childProperty);
1431 
1432     return childProperty;
1433 }
1434 
SetCell(int column,wxPGCell * cellObj)1435 void wxPGProperty::SetCell( int column, wxPGCell* cellObj )
1436 {
1437     if ( column >= (int)m_cells.size() )
1438         m_cells.SetCount(column+1, NULL);
1439 
1440     delete (wxPGCell*) m_cells[column];
1441     m_cells[column] = cellObj;
1442 }
1443 
GetOrCreateCell(unsigned int column)1444 wxPGCell* wxPGProperty::GetOrCreateCell( unsigned int column )
1445 {
1446     wxPGCell* cell = GetCell(column);
1447     if ( cell )
1448         return cell;
1449 
1450     wxString text;
1451     if ( column == 0 )
1452         text = m_label;
1453 
1454     cell = new wxPGCell(text);
1455     SetCell(column, cell);
1456     return cell;
1457 }
1458 
SetChoiceSelection(int newValue,const wxPGChoiceInfo & choiceInfo)1459 void wxPGProperty::SetChoiceSelection( int newValue,
1460                                        const wxPGChoiceInfo& choiceInfo )
1461 {
1462     // Changes value of a property with choices, but only
1463     // works if the value type is long or string.
1464     wxString ts = GetValue().GetType();
1465 
1466     wxCHECK_RET( choiceInfo.m_choices, wxT("invalid choiceinfo") );
1467 
1468     if ( ts == wxT("long") )
1469     {
1470         SetValue( (long) newValue );
1471     }
1472     else if ( ts == wxT("string") )
1473     {
1474         SetValue( choiceInfo.m_choices->GetLabel(newValue) );
1475     }
1476 }
1477 
1478 
GetChoiceString(unsigned int index)1479 wxString wxPGProperty::GetChoiceString( unsigned int index )
1480 {
1481     wxPGChoiceInfo ci;
1482     GetChoiceInfo(&ci);
1483     wxCHECK_MSG( ci.m_choices,
1484                  wxEmptyString,
1485                  wxT("This property class does not support choices.") );
1486     return ci.m_choices->GetLabel(index);
1487 }
1488 
InsertChoice(const wxString & label,int index,int value)1489 int wxPGProperty::InsertChoice( const wxString& label, int index, int value )
1490 {
1491     wxPropertyGrid* pg = GetGrid();
1492 
1493     wxPGChoiceInfo ci;
1494     ci.m_choices = (wxPGChoices*) NULL;
1495     int sel = GetChoiceInfo(&ci);
1496 
1497     wxCHECK_MSG( ci.m_choices,
1498                  -1,
1499                  wxT("This property class does not support choices.") );
1500 
1501     int newSel = sel;
1502 
1503     if ( index < 0 )
1504         index = ci.m_choices->GetCount();
1505 
1506     if ( index <= sel )
1507         newSel++;
1508 
1509     ci.m_choices->Insert(label, index, value);
1510 
1511     if ( sel != newSel )
1512         SetChoiceSelection(newSel, ci);
1513 
1514     if ( this == pg->GetSelection() )
1515         GetEditorClass()->InsertItem(pg->GetEditorControl(),label,index);
1516 
1517     return index;
1518 }
1519 
1520 
DeleteChoice(int index)1521 void wxPGProperty::DeleteChoice( int index )
1522 {
1523     wxPropertyGrid* pg = GetGrid();
1524 
1525     wxPGChoiceInfo ci;
1526     ci.m_choices = (wxPGChoices*) NULL;
1527     int sel = GetChoiceInfo(&ci);
1528 
1529     wxCHECK_RET( ci.m_choices,
1530                  wxT("This property class does not support choices.") );
1531 
1532     int newSel = sel;
1533 
1534     // Adjust current value
1535     if ( sel == index )
1536     {
1537         SetValueToUnspecified();
1538         newSel = 0;
1539     }
1540     else if ( index < sel )
1541     {
1542         newSel--;
1543     }
1544 
1545     ci.m_choices->RemoveAt(index);
1546 
1547     if ( sel != newSel )
1548         SetChoiceSelection(newSel, ci);
1549 
1550     if ( this == pg->GetSelection() )
1551         GetEditorClass()->DeleteItem(pg->GetEditorControl(), index);
1552 }
1553 
GetChoiceInfo(wxPGChoiceInfo * WXUNUSED (info))1554 int wxPGProperty::GetChoiceInfo( wxPGChoiceInfo* WXUNUSED(info) )
1555 {
1556     return -1;
1557 }
1558 
GetEditorDialog() const1559 wxPGEditorDialogAdapter* wxPGProperty::GetEditorDialog() const
1560 {
1561     return NULL;
1562 }
1563 
DoSetAttribute(const wxString & WXUNUSED (name),wxVariant & WXUNUSED (value))1564 bool wxPGProperty::DoSetAttribute( const wxString& WXUNUSED(name), wxVariant& WXUNUSED(value) )
1565 {
1566     return false;
1567 }
1568 
SetAttribute(const wxString & name,wxVariant value)1569 void wxPGProperty::SetAttribute( const wxString& name, wxVariant value )
1570 {
1571     if ( DoSetAttribute( name, value ) )
1572     {
1573         // Support working without grid, when possible
1574         if ( wxPGGlobalVars->HasExtraStyle( wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES ) )
1575             return;
1576     }
1577 
1578     m_attributes.Set( name, value );
1579 }
1580 
DoGetAttribute(const wxString & WXUNUSED (name)) const1581 wxVariant wxPGProperty::DoGetAttribute( const wxString& WXUNUSED(name) ) const
1582 {
1583     return wxVariant();
1584 }
1585 
1586 
GetAttribute(const wxString & name) const1587 wxVariant wxPGProperty::GetAttribute( const wxString& name ) const
1588 {
1589     return m_attributes.FindValue(name);
1590 }
1591 
GetAttribute(const wxString & name,const wxString & defVal) const1592 wxString wxPGProperty::GetAttribute( const wxString& name, const wxString& defVal ) const
1593 {
1594     wxVariant variant = m_attributes.FindValue(name);
1595 
1596     if ( !variant.IsNull() )
1597         return variant.GetString();
1598 
1599     return defVal;
1600 }
1601 
GetAttributeAsLong(const wxString & name,long defVal) const1602 long wxPGProperty::GetAttributeAsLong( const wxString& name, long defVal ) const
1603 {
1604     wxVariant variant = m_attributes.FindValue(name);
1605 
1606     return wxPGVariantToInt(variant, defVal);
1607 }
1608 
GetAttributeAsDouble(const wxString & name,double defVal) const1609 double wxPGProperty::GetAttributeAsDouble( const wxString& name, double defVal ) const
1610 {
1611     double retVal;
1612     wxVariant variant = m_attributes.FindValue(name);
1613 
1614     if ( wxPGVariantToDouble(variant, &retVal) )
1615         return retVal;
1616 
1617     return defVal;
1618 }
1619 
GetAttributesAsList() const1620 wxVariant wxPGProperty::GetAttributesAsList() const
1621 {
1622     wxVariantList tempList;
1623     wxVariant v( tempList, wxString::Format(wxT("@%s@attr"),m_name.c_str()) );
1624 
1625     wxPGAttributeStorage::const_iterator it = m_attributes.StartIteration();
1626     wxVariant variant;
1627 
1628     while ( m_attributes.GetNext(it, variant) )
1629         v.Append(variant);
1630 
1631     return v;
1632 }
1633 
1634 // Slots of utility flags are NULL
1635 const unsigned int gs_propFlagToStringSize = 14;
1636 
1637 static const wxChar* gs_propFlagToString[gs_propFlagToStringSize] = {
1638     NULL,
1639     wxT("DISABLED"),
1640     wxT("HIDDEN"),
1641     NULL,
1642     wxT("NOEDITOR"),
1643     wxT("COLLAPSED"),
1644     NULL,
1645     NULL,
1646     NULL,
1647     NULL,
1648     NULL,
1649     NULL,
1650     NULL,
1651     NULL
1652 };
1653 
GetFlagsAsString(FlagType flagsMask) const1654 wxString wxPGProperty::GetFlagsAsString( FlagType flagsMask ) const
1655 {
1656     wxString s;
1657     int relevantFlags = m_flags & flagsMask & wxPG_STRING_STORED_FLAGS;
1658     FlagType a = 1;
1659 
1660     unsigned int i = 0;
1661     for ( i=0; i<gs_propFlagToStringSize; i++ )
1662     {
1663         if ( relevantFlags & a )
1664         {
1665             const wxChar* fs = gs_propFlagToString[i];
1666             wxASSERT(fs);
1667             if ( s.length() )
1668                 s << wxT("|");
1669             s << fs;
1670         }
1671         a = a << 1;
1672     }
1673 
1674     return s;
1675 }
1676 
SetFlagsFromString(const wxString & str)1677 void wxPGProperty::SetFlagsFromString( const wxString& str )
1678 {
1679     FlagType flags = 0;
1680 
1681     WX_PG_TOKENIZER1_BEGIN(str, wxT('|'))
1682         unsigned int i;
1683         for ( i=0; i<gs_propFlagToStringSize; i++ )
1684         {
1685             const wxChar* fs = gs_propFlagToString[i];
1686             if ( fs && str == fs )
1687             {
1688                 flags |= (1<<i);
1689                 break;
1690             }
1691         }
1692     WX_PG_TOKENIZER1_END()
1693 
1694     m_flags = (m_flags & ~wxPG_STRING_STORED_FLAGS) | flags;
1695 }
1696 
DoGetValidator() const1697 wxValidator* wxPGProperty::DoGetValidator() const
1698 {
1699     return (wxValidator*) NULL;
1700 }
1701 
GetChoices()1702 wxPGChoices& wxPGProperty::GetChoices()
1703 {
1704     wxPGChoiceInfo choiceInfo;
1705     choiceInfo.m_choices = NULL;
1706     GetChoiceInfo(&choiceInfo);
1707     wxASSERT_MSG( choiceInfo.m_choices,
1708                   wxT("This property class does not support choices.") );
1709     return *choiceInfo.m_choices;
1710 }
1711 
GetChoices() const1712 const wxPGChoices& wxPGProperty::GetChoices() const
1713 {
1714     return (const wxPGChoices&) ((wxPGProperty*)this)->GetChoices();
1715 }
1716 
GetChoiceCount() const1717 unsigned int wxPGProperty::GetChoiceCount() const
1718 {
1719     wxPGChoiceInfo ci;
1720     ci.m_choices = NULL;
1721     ((wxPGProperty*)this)->GetChoiceInfo(&ci);
1722     if ( ci.m_choices && ci.m_choices->IsOk() )
1723         return ci.m_choices->GetCount();
1724     return 0;
1725 }
1726 
GetCurrentChoice() const1727 const wxPGChoiceEntry* wxPGProperty::GetCurrentChoice() const
1728 {
1729     wxPGChoiceInfo ci;
1730     ci.m_choices = (wxPGChoices*) NULL;
1731     int index = ((wxPGProperty*)this)->GetChoiceInfo(&ci);
1732     if ( index == -1 || !ci.m_choices || index >= (int)ci.m_choices->GetCount() )
1733         return NULL;
1734 
1735     return &(*ci.m_choices)[index];
1736 }
1737 
SetChoices(wxPGChoices & choices)1738 bool wxPGProperty::SetChoices( wxPGChoices& choices )
1739 {
1740     wxPGChoiceInfo ci;
1741     ci.m_choices = NULL;
1742 
1743     GetChoiceInfo(&ci);
1744     wxCHECK_MSG( ci.m_choices,
1745                  false,
1746                  wxT("This property class does not support choices.") );
1747 
1748     // Property must be de-selected first (otherwise choices in
1749     // the control would be de-synced with true choices)
1750     wxPropertyGrid* pg = GetGrid();
1751     if ( pg && pg->GetSelection() == this )
1752         pg->ClearSelection();
1753 
1754     ci.m_choices->Assign(choices);
1755 
1756     // This may be needed to trigger some initialization
1757     // (but don't do it if property is somewhat uninitialized)
1758     wxVariant defVal = GetDefaultValue();
1759     if ( defVal.IsNull() )
1760         return false;
1761 
1762     SetValue(defVal);
1763 
1764     return true;
1765 }
1766 
1767 
GetEditorClass() const1768 const wxPGEditor* wxPGProperty::GetEditorClass() const
1769 {
1770     const wxPGEditor* editor;
1771 
1772     if ( !m_customEditor )
1773     {
1774 #ifdef __WXPYTHON__
1775         wxString editorName = GetEditor();
1776         if ( editorName.length() )
1777             editor = wxPropertyGridInterface::GetEditorByName(editorName);
1778         else
1779 #endif
1780             editor = DoGetEditorClass();
1781     }
1782     else
1783         editor = m_customEditor;
1784 
1785     //
1786     // Maybe override editor if common value specified
1787     if ( GetDisplayedCommonValueCount() )
1788     {
1789         // TextCtrlAndButton -> ComboBoxAndButton
1790         if ( editor->IsKindOf(CLASSINFO(wxPGTextCtrlAndButtonEditor)) )
1791             editor = wxPG_EDITOR(ChoiceAndButton);
1792 
1793         // TextCtrl -> ComboBox
1794         else if ( editor->IsKindOf(CLASSINFO(wxPGTextCtrlEditor)) )
1795             editor = wxPG_EDITOR(ComboBox);
1796     }
1797 
1798     return editor;
1799 }
1800 
1801 
1802 // Privatizes set of choices
SetChoicesExclusive()1803 void wxPGProperty::SetChoicesExclusive()
1804 {
1805     wxPGChoiceInfo ci;
1806     ci.m_choices = (wxPGChoices*) NULL;
1807 
1808     GetChoiceInfo(&ci);
1809     if ( ci.m_choices )
1810         ci.m_choices->SetExclusive();
1811 }
1812 
Hide(bool hide,int flags)1813 bool wxPGProperty::Hide( bool hide, int flags )
1814 {
1815     wxPropertyGrid* pg = GetGrid();
1816     if ( pg )
1817         return pg->HideProperty(this, hide, flags);
1818 
1819     return DoHide( hide, flags );
1820 }
1821 
DoHide(bool hide,int flags)1822 bool wxPGProperty::DoHide( bool hide, int flags )
1823 {
1824     if ( !hide )
1825         ClearFlag( wxPG_PROP_HIDDEN );
1826     else
1827         SetFlag( wxPG_PROP_HIDDEN );
1828 
1829     if ( flags & wxPG_RECURSE )
1830     {
1831         unsigned int i;
1832         for ( i = 0; i < GetChildCount(); i++ )
1833             Item(i)->DoHide(hide, flags | wxPG_RECURSE_STARTS);
1834     }
1835 
1836     return true;
1837 }
1838 
HasVisibleChildren() const1839 bool wxPGProperty::HasVisibleChildren() const
1840 {
1841     unsigned int i;
1842 
1843     for ( i=0; i<GetChildCount(); i++ )
1844     {
1845         wxPGProperty* child = Item(i);
1846 
1847         if ( !child->HasFlag(wxPG_PROP_HIDDEN) )
1848             return true;
1849     }
1850 
1851     return false;
1852 }
1853 
PrepareValueForDialogEditing(wxPropertyGrid * propGrid)1854 bool wxPGProperty::PrepareValueForDialogEditing( wxPropertyGrid* propGrid )
1855 {
1856     return propGrid->EditorValidate();
1857 }
1858 
1859 
RecreateEditor()1860 bool wxPGProperty::RecreateEditor()
1861 {
1862     wxPropertyGrid* pg = GetGrid();
1863     wxASSERT(pg);
1864 
1865     wxPGProperty* selected = pg->GetSelection();
1866     if ( this == selected )
1867     {
1868         pg->DoSelectProperty(this, wxPG_SEL_FORCE);
1869         return true;
1870     }
1871     return false;
1872 }
1873 
1874 
SetValueImage(wxBitmap & bmp)1875 void wxPGProperty::SetValueImage( wxBitmap& bmp )
1876 {
1877     delete m_valueBitmap;
1878 
1879     if ( bmp.Ok() )
1880     {
1881         // Resize the image
1882         wxSize maxSz = GetGrid()->GetImageSize();
1883         wxSize imSz(bmp.GetWidth(),bmp.GetHeight());
1884 
1885         if ( imSz.y > maxSz.y )
1886         {
1887         #if wxUSE_IMAGE
1888             // Here we use high-quality wxImage scaling functions available
1889             // in wxWidgets 2.8
1890             wxImage img = bmp.ConvertToImage();
1891             double scaleY = (double)maxSz.y / (double)imSz.y;
1892             img.Rescale(((double)bmp.GetWidth())*scaleY,
1893                         ((double)bmp.GetHeight())*scaleY,
1894                         wxIMAGE_QUALITY_HIGH);
1895             wxBitmap* bmpNew = new wxBitmap(img, 32);
1896         #else
1897             // This is the old, deprecated method of scaling the image, but
1898             // should be more compatible with older versions of wx
1899             // Create a memory DC
1900             wxBitmap* bmpNew = new wxBitmap(maxSz.x,maxSz.y,bmp.GetDepth());
1901 
1902             wxMemoryDC dc;
1903             dc.SelectObject(*bmpNew);
1904 
1905             // Scale
1906             double scaleY = (double)maxSz.y / (double)imSz.y;
1907 
1908             dc.SetUserScale(scaleY, scaleY);
1909 
1910             dc.DrawBitmap(bmp, 0, 0);
1911         #endif
1912 
1913             m_valueBitmap = bmpNew;
1914         }
1915         else
1916         {
1917             m_valueBitmap = new wxBitmap(bmp);
1918         }
1919 
1920         m_flags |= wxPG_PROP_CUSTOMIMAGE;
1921     }
1922     else
1923     {
1924         m_valueBitmap = NULL;
1925         m_flags &= ~(wxPG_PROP_CUSTOMIMAGE);
1926     }
1927 }
1928 
1929 
GetMainParent() const1930 wxPGProperty* wxPGProperty::GetMainParent() const
1931 {
1932     const wxPGProperty* curChild = this;
1933     const wxPGProperty* curParent = m_parent;
1934 
1935     while ( curParent && !curParent->IsCategory() )
1936     {
1937         curChild = curParent;
1938         curParent = curParent->m_parent;
1939     }
1940 
1941     return (wxPGProperty*) curChild;
1942 }
1943 
1944 
GetLastVisibleSubItem() const1945 const wxPGProperty* wxPGProperty::GetLastVisibleSubItem() const
1946 {
1947     //
1948     // Returns last visible sub-item, recursively.
1949     if ( !IsExpanded() || !GetChildCount() )
1950         return this;
1951 
1952     return Last()->GetLastVisibleSubItem();
1953 }
1954 
1955 
IsVisible() const1956 bool wxPGProperty::IsVisible() const
1957 {
1958     const wxPGProperty* parent;
1959 
1960     if ( HasFlag(wxPG_PROP_HIDDEN) )
1961         return false;
1962 
1963     for ( parent = GetParent(); parent != NULL; parent = parent->GetParent() )
1964     {
1965         if ( !parent->IsExpanded() || parent->HasFlag(wxPG_PROP_HIDDEN) )
1966             return false;
1967     }
1968 
1969     return true;
1970 }
1971 
GetGridIfDisplayed() const1972 wxPropertyGrid* wxPGProperty::GetGridIfDisplayed() const
1973 {
1974     wxPropertyGridState* state = GetParentState();
1975     if ( !state )
1976         return NULL;
1977     wxPropertyGrid* propGrid = state->GetGrid();
1978     if ( state == propGrid->GetState() )
1979         return propGrid;
1980     return NULL;
1981 }
1982 
1983 
GetY2(int lh) const1984 int wxPGProperty::GetY2( int lh ) const
1985 {
1986     const wxPGProperty* parent;
1987     const wxPGProperty* child = this;
1988 
1989     int y = 0;
1990 
1991     for ( parent = GetParent(); parent != NULL; parent = child->GetParent() )
1992     {
1993         if ( !parent->IsExpanded() )
1994 /* C::B begin */
1995             return parent->GetY2(lh);
1996 /* C::B end */
1997         y += parent->GetChildrenHeight(lh, child->GetIndexInParent());
1998         y += lh;
1999         child = parent;
2000     }
2001 
2002     y -= lh;  // need to reduce one level
2003 
2004     return y;
2005 }
2006 
2007 
GetY() const2008 int wxPGProperty::GetY() const
2009 {
2010     return GetY2(GetGrid()->GetRowHeight());
2011 }
2012 
2013 
GetPtr(wxPropertyGridInterface * methods) const2014 wxPGProperty* wxPGPropArgCls::GetPtr( wxPropertyGridInterface* methods ) const
2015 {
2016     if ( !m_isName )
2017     {
2018         wxASSERT_MSG( m_ptr.property, wxT("invalid property ptr") );
2019         return m_ptr.property;
2020     }
2021     else if ( m_isName == 1 )
2022         return methods->GetPropertyByNameI(*m_ptr.name);
2023     else if ( m_isName == 2 )
2024         return methods->GetPropertyByNameI(m_ptr.rawname);
2025 #ifdef __WXPYTHON__
2026     else if ( m_isName == 3 )
2027         return methods->GetPropertyByNameI(*m_ptr.name);
2028 #endif
2029 
2030     wxASSERT( m_isName <= 3 );
2031     return NULL;
2032 }
2033 
2034 // This is used by Insert etc.
AddChild2(wxPGProperty * prop,int index,bool correct_mode)2035 void wxPGProperty::AddChild2( wxPGProperty* prop, int index, bool correct_mode )
2036 {
2037     if ( index < 0 || (size_t)index >= m_children.GetCount() )
2038     {
2039         if ( correct_mode ) prop->m_arrIndex = m_children.GetCount();
2040         m_children.Add( prop );
2041     }
2042     else
2043     {
2044         m_children.Insert( prop, index );
2045         if ( correct_mode ) FixIndexesOfChildren( index );
2046     }
2047 
2048     prop->m_parent = this;
2049 }
2050 
2051 // This is used by properties that have fixed sub-properties
AddChild(wxPGProperty * prop)2052 void wxPGProperty::AddChild( wxPGProperty* prop )
2053 {
2054     wxASSERT_MSG( prop->GetBaseName().length(),
2055                   wxT("Property's children must have unique, non-empty names within their scope") );
2056 
2057     prop->m_arrIndex = m_children.GetCount();
2058     m_children.Add( prop );
2059 
2060     int custImgHeight = prop->OnMeasureImage().y;
2061     if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
2062         prop->m_flags |= wxPG_PROP_CUSTOMIMAGE;
2063 
2064     prop->m_parent = this;
2065 }
2066 
2067 
AdaptListToValue(wxVariant & list,wxVariant * value) const2068 void wxPGProperty::AdaptListToValue( wxVariant& list, wxVariant* value ) const
2069 {
2070     wxASSERT( GetChildCount() );
2071     wxASSERT( !IsCategory() );
2072 
2073     *value = GetValue();
2074 
2075     if ( !list.GetCount() )
2076         return;
2077 
2078     wxASSERT( GetCount() >= (unsigned int)list.GetCount() );
2079 
2080     bool allChildrenSpecified;
2081 
2082     // Don't fully update aggregate properties unless all children have
2083     // specified value
2084     if ( HasFlag(wxPG_PROP_AGGREGATE) )
2085         allChildrenSpecified = AreAllChildrenSpecified(&list);
2086     else
2087         allChildrenSpecified = true;
2088 
2089     wxVariant childValue = list[0];
2090     unsigned int i;
2091     unsigned int n = 0;
2092 
2093     //wxLogDebug(wxT(">> %s.AdaptListToValue()"),GetBaseName().c_str());
2094 
2095     for ( i=0; i<GetCount(); i++ )
2096     {
2097         const wxPGProperty* child = Item(i);
2098 
2099         if ( childValue.GetName() == child->GetBaseName() )
2100         {
2101             //wxLogDebug(wxT("  %s(n=%i), %s"),childValue.GetName().c_str(),n,childValue.GetType().c_str());
2102 
2103             if ( wxPGIsVariantType(childValue, list) )
2104             {
2105                 wxVariant cv2(child->GetValue());
2106                 child->AdaptListToValue(childValue, &cv2);
2107                 childValue = cv2;
2108             }
2109 
2110             if ( allChildrenSpecified )
2111                 ActualChildChanged(*value, i, childValue);
2112             n++;
2113             if ( n == (unsigned int)list.GetCount() )
2114                 break;
2115             childValue = list[n];
2116         }
2117     }
2118 }
2119 
2120 
FixIndexesOfChildren(unsigned int starthere)2121 void wxPGProperty::FixIndexesOfChildren( unsigned int starthere )
2122 {
2123     unsigned int i;
2124     for ( i=starthere;i<GetCount();i++)
2125         Item(i)->m_arrIndex = i;
2126 }
2127 
2128 
2129 // Returns (direct) child property with given name (or NULL if not found)
GetPropertyByName(const wxString & name) const2130 wxPGProperty* wxPGProperty::GetPropertyByName( const wxString& name ) const
2131 {
2132     size_t i;
2133 
2134     for ( i=0; i<GetCount(); i++ )
2135     {
2136         wxPGProperty* p = Item(i);
2137         if ( p->m_name == name )
2138             return p;
2139     }
2140 
2141     // Does it have point, then?
2142     int pos = name.Find(wxT('.'));
2143     if ( pos <= 0 )
2144         return (wxPGProperty*) NULL;
2145 
2146     wxPGProperty* p = GetPropertyByName(name. substr(0,pos));
2147 
2148     if ( !p || !p->GetChildCount() )
2149         return NULL;
2150 
2151     return p->GetPropertyByName(name.substr(pos+1,name.length()-pos-1));
2152 }
2153 
GetPropertyByNameWH(const wxString & name,unsigned int hintIndex) const2154 wxPGProperty* wxPGProperty::GetPropertyByNameWH( const wxString& name, unsigned int hintIndex ) const
2155 {
2156     unsigned int i = hintIndex;
2157 
2158     if ( i >= GetCount() )
2159         i = 0;
2160 
2161     unsigned int lastIndex = i - 1;
2162 
2163     if ( lastIndex >= GetCount() )
2164         lastIndex = GetCount() - 1;
2165 
2166     for (;;)
2167     {
2168         wxPGProperty* p = Item(i);
2169         if ( p->m_name == name )
2170             return p;
2171 
2172         if ( i == lastIndex )
2173             break;
2174 
2175         i++;
2176         if ( i == GetCount() )
2177             i = 0;
2178     };
2179 
2180     return NULL;
2181 }
2182 
GetChildrenHeight(int lh,int iMax_) const2183 int wxPGProperty::GetChildrenHeight( int lh, int iMax_ ) const
2184 {
2185     // Returns height of children, recursively, and
2186     // by taking expanded/collapsed status into account.
2187     //
2188     // iMax is used when finding property y-positions.
2189     //
2190     unsigned int i = 0;
2191     int h = 0;
2192 
2193     if ( iMax_ == -1 )
2194         iMax_ = GetChildCount();
2195 
2196     unsigned int iMax = iMax_;
2197 
2198     wxASSERT( iMax <= GetChildCount() );
2199 
2200     if ( !IsExpanded() && GetParent() )
2201         return 0;
2202 
2203     while ( i < iMax )
2204     {
2205         wxPGProperty* pwc = (wxPGProperty*) Item(i);
2206 
2207         if ( !pwc->HasFlag(wxPG_PROP_HIDDEN) )
2208         {
2209             if ( !pwc->IsExpanded() ||
2210                  pwc->GetChildCount() == 0 )
2211                 h += lh;
2212             else
2213                 h += pwc->GetChildrenHeight(lh) + lh;
2214         }
2215 
2216         i++;
2217     }
2218 
2219     return h;
2220 }
2221 
GetItemAtY(unsigned int y,unsigned int lh,unsigned int * nextItemY) const2222 wxPGProperty* wxPGProperty::GetItemAtY( unsigned int y, unsigned int lh, unsigned int* nextItemY ) const
2223 {
2224     wxASSERT( nextItemY );
2225 
2226     // Linear search at the moment
2227     //
2228     // nextItemY = y of next visible property, final value will be written back.
2229     wxPGProperty* result = NULL;
2230     wxPGProperty* current = NULL;
2231     unsigned int iy = *nextItemY;
2232     unsigned int i = 0;
2233     unsigned int iMax = GetCount();
2234 
2235     while ( i < iMax )
2236     {
2237         wxPGProperty* pwc = Item(i);
2238 
2239         if ( !pwc->HasFlag(wxPG_PROP_HIDDEN) )
2240         {
2241             // Found?
2242             if ( y < iy )
2243             {
2244                 result = current;
2245                 break;
2246             }
2247 
2248             iy += lh;
2249 
2250             if ( pwc->IsExpanded() &&
2251                  pwc->GetChildCount() > 0 )
2252             {
2253                 result = (wxPGProperty*) pwc->GetItemAtY( y, lh, &iy );
2254                 if ( result )
2255                     break;
2256             }
2257 
2258             current = pwc;
2259         }
2260 
2261         i++;
2262     }
2263 
2264     // Found?
2265     if ( !result && y < iy )
2266         result = current;
2267 
2268     *nextItemY = iy;
2269 
2270     /*
2271     if ( current )
2272         wxLogDebug(wxT("%s::GetItemAtY(%i) -> %s"),this->GetLabel().c_str(),y,current->GetLabel().c_str());
2273     else
2274         wxLogDebug(wxT("%s::GetItemAtY(%i) -> NULL"),this->GetLabel().c_str(),y);
2275     */
2276 
2277     return (wxPGProperty*) result;
2278 }
2279 
DoEmpty()2280 void wxPGProperty::DoEmpty()
2281 {
2282     size_t i;
2283     if ( !HasFlag(wxPG_PROP_CHILDREN_ARE_COPIES) )
2284     {
2285         for ( i=0; i<GetCount(); i++ )
2286         {
2287             wxPGProperty* p = (wxPGProperty*) Item(i);
2288             delete p;
2289         }
2290     }
2291 
2292     m_children.Empty();
2293 }
2294 
DeleteChildren()2295 void wxPGProperty::DeleteChildren()
2296 {
2297     wxPropertyGridState* state = m_parentState;
2298 
2299     if ( !GetChildCount() )
2300         return;
2301 
2302     // Because deletion is sometimes deferred, we have to use
2303     // this sort of code for enumerating the child properties.
2304     unsigned int i = GetChildCount();
2305     while ( i > 0 )
2306     {
2307         i--;
2308         state->DoDelete(Item(i), true);
2309     }
2310 }
2311 
IsChildSelected(const bool recursive) const2312 bool wxPGProperty::IsChildSelected( const bool recursive ) const
2313 {
2314     size_t i;
2315     for ( i = 0; i < GetChildCount(); i++ )
2316     {
2317         wxPGProperty* child = Item(i);
2318 
2319         // Test child
2320         if ( m_parentState->DoIsPropertySelected( child ) )
2321             return true;
2322 
2323         // Test sub-childs
2324         if ( recursive && child->IsChildSelected( recursive ) )
2325             return true;
2326     }
2327 
2328     return false;
2329 }
2330 
ChildChanged(wxVariant & WXUNUSED (thisValue),int WXUNUSED (childIndex),wxVariant & WXUNUSED (childValue)) const2331 void wxPGProperty::ChildChanged( wxVariant& WXUNUSED(thisValue),
2332                                  int WXUNUSED(childIndex),
2333                                  wxVariant& WXUNUSED(childValue) ) const
2334 {
2335 }
2336 
AreAllChildrenSpecified(wxVariant * pendingList) const2337 bool wxPGProperty::AreAllChildrenSpecified( wxVariant* pendingList ) const
2338 {
2339     unsigned int i;
2340 
2341     const wxVariantList* pList = NULL;
2342     wxVariantList::const_iterator node;
2343 
2344     if ( pendingList )
2345     {
2346         pList = &pendingList->GetList();
2347         node = pList->begin();
2348     }
2349 
2350     for ( i=0; i<GetChildCount(); i++ )
2351     {
2352         wxPGProperty* child = Item(i);
2353         const wxVariant* listValue = NULL;
2354         wxVariant value;
2355 
2356         if ( pendingList )
2357         {
2358             const wxString& childName = child->GetBaseName();
2359 
2360             for ( ; node != pList->end(); node++ )
2361             {
2362                 const wxVariant& item = *((const wxVariant*)*node);
2363                 if ( item.GetName() == childName )
2364                 {
2365                     listValue = &item;
2366                     value = item;
2367                     break;
2368                 }
2369             }
2370         }
2371 
2372         if ( !listValue )
2373             value = child->GetValue();
2374 
2375         if ( value.IsNull() )
2376             return false;
2377 
2378         // Check recursively
2379         if ( child->GetChildCount() )
2380         {
2381             const wxVariant* childList = NULL;
2382 
2383             if ( listValue && wxPGIsVariantType(*listValue, list) )
2384                 childList = listValue;
2385 
2386             if ( !child->AreAllChildrenSpecified((wxVariant*)childList) )
2387                 return false;
2388         }
2389     }
2390 
2391     return true;
2392 }
2393 
UpdateParentValues()2394 wxPGProperty* wxPGProperty::UpdateParentValues()
2395 {
2396     wxPGProperty* parent = m_parent;
2397     if ( parent && parent->HasFlag(wxPG_PROP_COMPOSED_VALUE) &&
2398          !parent->IsCategory() && !parent->IsRoot() )
2399     {
2400         wxString s;
2401         parent->GenerateComposedValue(s, 0);
2402         parent->m_value = s;
2403         return parent->UpdateParentValues();
2404     }
2405     return this;
2406 }
2407 
IsTextEditable() const2408 bool wxPGProperty::IsTextEditable() const
2409 {
2410     if ( HasFlag(wxPG_PROP_READONLY) )
2411         return false;
2412 
2413     if ( HasFlag(wxPG_PROP_NOEDITOR) &&
2414          (GetChildCount() ||
2415           wxPG_String_EndsWith(wxString(GetEditorClass()->GetClassInfo()->GetClassName()), wxT("Button")))
2416        )
2417         return false;
2418 
2419     return true;
2420 }
2421 
2422 // -----------------------------------------------------------------------
2423 // wxPGRootProperty
2424 // -----------------------------------------------------------------------
2425 
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPGRootProperty,none,TextCtrl)2426 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPGRootProperty,none,TextCtrl)
2427 IMPLEMENT_DYNAMIC_CLASS(wxPGRootProperty, wxPGProperty)
2428 
2429 
2430 wxPGRootProperty::wxPGRootProperty()
2431     : wxPGProperty()
2432 {
2433 #ifdef __WXDEBUG__
2434     m_name = wxT("<root>");
2435 #endif
2436     SetParentalType(0);
2437     m_depth = 0;
2438 }
2439 
2440 
~wxPGRootProperty()2441 wxPGRootProperty::~wxPGRootProperty()
2442 {
2443 }
2444 
2445 
2446 // -----------------------------------------------------------------------
2447 // wxPropertyCategory
2448 // -----------------------------------------------------------------------
2449 
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPropertyCategory,none,TextCtrl)2450 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPropertyCategory,none,TextCtrl)
2451 IMPLEMENT_DYNAMIC_CLASS(wxPropertyCategory, wxPGProperty)
2452 
2453 void wxPropertyCategory::Init()
2454 {
2455     // don't set colour - prepareadditem method should do this
2456     SetParentalType(wxPG_PROP_CATEGORY);
2457     m_capFgColIndex = 1;
2458     m_textExtent = -1;
2459 }
2460 
wxPropertyCategory()2461 wxPropertyCategory::wxPropertyCategory()
2462     : wxPGProperty()
2463 {
2464     Init();
2465 }
2466 
2467 
wxPropertyCategory(const wxString & label,const wxString & name)2468 wxPropertyCategory::wxPropertyCategory( const wxString &label, const wxString& name )
2469     : wxPGProperty(label,name)
2470 {
2471     Init();
2472 }
2473 
2474 
~wxPropertyCategory()2475 wxPropertyCategory::~wxPropertyCategory()
2476 {
2477 }
2478 
2479 
GetValueAsString(int) const2480 wxString wxPropertyCategory::GetValueAsString( int ) const
2481 {
2482     return wxEmptyString;
2483 }
2484 
GetTextExtent(const wxWindow * wnd,const wxFont & font) const2485 int wxPropertyCategory::GetTextExtent( const wxWindow* wnd, const wxFont& font ) const
2486 {
2487     if ( m_textExtent > 0 )
2488         return m_textExtent;
2489     int x = 0, y = 0;
2490 	((wxWindow*)wnd)->GetTextExtent( m_label, &x, &y, 0, 0, &font );
2491     return x;
2492 }
2493 
CalculateTextExtent(wxWindow * wnd,const wxFont & font)2494 void wxPropertyCategory::CalculateTextExtent( wxWindow* wnd, const wxFont& font )
2495 {
2496     int x = 0, y = 0;
2497 	wnd->GetTextExtent( m_label, &x, &y, 0, 0, &font );
2498     m_textExtent = x;
2499 }
2500 
2501 // -----------------------------------------------------------------------
2502 // wxPGCellRenderer
2503 // -----------------------------------------------------------------------
2504 
GetImageSize(const wxPGProperty * WXUNUSED (property),int WXUNUSED (column),int WXUNUSED (item)) const2505 wxSize wxPGCellRenderer::GetImageSize( const wxPGProperty* WXUNUSED(property),
2506                                        int WXUNUSED(column),
2507                                        int WXUNUSED(item) ) const
2508 {
2509      return wxSize(0, 0);
2510 }
2511 
DrawText(wxDC & dc,const wxRect & rect,int xOffset,const wxString & text) const2512 void wxPGCellRenderer::DrawText( wxDC& dc, const wxRect& rect,
2513                                  int xOffset, const wxString& text ) const
2514 {
2515     //if ( xOffset )
2516     //    xOffset += DEFAULT_IMAGE_OFFSET_INCREMENT;
2517     dc.DrawText( text,
2518                  rect.x+xOffset+wxPG_XBEFORETEXT,
2519                  rect.y+((rect.height-dc.GetCharHeight())/2) );
2520 }
2521 
DrawEditorValue(wxDC & dc,const wxRect & rect,int xOffset,const wxString & text,wxPGProperty * property,const wxPGEditor * editor) const2522 void wxPGCellRenderer::DrawEditorValue( wxDC& dc, const wxRect& rect,
2523                                         int xOffset, const wxString& text,
2524                                         wxPGProperty* property,
2525                                         const wxPGEditor* editor ) const
2526 {
2527     int yOffset = ((rect.height-dc.GetCharHeight())/2);
2528 
2529     if ( editor )
2530     {
2531         wxRect rect2(rect);
2532         rect2.x += xOffset;
2533         rect2.y += yOffset;
2534         rect2.height -= yOffset;
2535         editor->DrawValue( dc, rect2, property, text );
2536     }
2537     else
2538     {
2539         dc.DrawText( text,
2540                      rect.x+xOffset+wxPG_XBEFORETEXT,
2541                      rect.y+yOffset );
2542     }
2543 }
2544 
DrawCaptionSelectionRect(wxDC & dc,int x,int y,int w,int h) const2545 void wxPGCellRenderer::DrawCaptionSelectionRect( wxDC& dc, int x, int y, int w, int h ) const
2546 {
2547     wxRect focusRect(x,y+((h-dc.GetCharHeight())/2),w,h);
2548     wxPGDrawFocusRect(dc,focusRect);
2549 }
2550 
PreDrawCell(wxDC & dc,const wxRect & rect,const wxPGCell & cell,int flags) const2551 int wxPGCellRenderer::PreDrawCell( wxDC& dc, const wxRect& rect, const wxPGCell& cell, int flags ) const
2552 {
2553     int imageWidth = 0;
2554 
2555     if ( !(flags & Selected) )
2556     {
2557         // Draw using wxPGCell information, if available
2558         wxColour fgCol = cell.GetFgCol();
2559         if ( fgCol.Ok() )
2560             dc.SetTextForeground(fgCol);
2561 
2562         wxColour bgCol = cell.GetBgCol();
2563         if ( bgCol.Ok() )
2564         {
2565             dc.SetPen(bgCol);
2566             dc.SetBrush(bgCol);
2567             dc.DrawRectangle(rect);
2568         }
2569     }
2570 
2571     // Use cell font, if provided
2572     const wxFont& font = cell.GetFont();
2573     if ( font.Ok() )
2574         dc.SetFont(font);
2575 
2576     const wxBitmap& bmp = cell.GetBitmap();
2577     if ( bmp.Ok() &&
2578         // In control, do not draw oversized bitmap
2579          (!(flags & Control) || bmp.GetHeight() < rect.height )
2580         )
2581     {
2582         dc.DrawBitmap( bmp,
2583                        rect.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
2584                        rect.y + wxPG_CUSTOM_IMAGE_SPACINGY,
2585                        true );
2586         imageWidth = bmp.GetWidth();
2587     }
2588 
2589     return imageWidth;
2590 }
2591 
PostDrawCell(wxDC & dc,const wxPropertyGrid * propGrid,const wxPGCell & cell,int WXUNUSED (flags)) const2592 void wxPGCellRenderer::PostDrawCell( wxDC& dc,
2593                                      const wxPropertyGrid* propGrid,
2594                                      const wxPGCell& cell,
2595                                      int WXUNUSED(flags) ) const
2596 {
2597     // Revert font
2598     const wxFont& font = cell.GetFont();
2599     if ( font.Ok() )
2600         dc.SetFont(propGrid->GetFont());
2601 }
2602 
2603 // -----------------------------------------------------------------------
2604 // wxPGDefaultRenderer
2605 // -----------------------------------------------------------------------
2606 
Render(wxDC & dc,const wxRect & rect,const wxPropertyGrid * propertyGrid,wxPGProperty * property,int column,int item,int flags) const2607 void wxPGDefaultRenderer::Render( wxDC& dc, const wxRect& rect,
2608                                   const wxPropertyGrid* propertyGrid, wxPGProperty* property,
2609                                   int column, int item, int flags ) const
2610 {
2611     bool isUnspecified = property->IsValueUnspecified();
2612 
2613     if ( column == 1 && item == -1 )
2614     {
2615         int cmnVal = property->GetCommonValue();
2616         if ( cmnVal >= 0 )
2617         {
2618             // Common Value
2619             if ( !isUnspecified )
2620                 DrawText( dc, rect, 0, propertyGrid->GetCommonValueLabel(cmnVal) );
2621             return;
2622         }
2623     }
2624 
2625     const wxPGEditor* editor = NULL;
2626     const wxPGCell* cell = property->GetCell(column);
2627 
2628     wxString text;
2629     int imageWidth = 0;
2630 
2631     if ( column == 1 )
2632     {
2633         if ( !(flags & Control) )
2634         {
2635             // Use special unspecified value cell
2636             if ( property->IsValueUnspecified() )
2637                 cell = &propertyGrid->GetUnspecifiedValueAppearance();
2638         }
2639 
2640         if ( !cell )
2641         {
2642             // Use choice cell?
2643             const wxPGCell* ccell = property->GetCurrentChoice();
2644             if ( ccell &&
2645                  ( wxGDI_IS_OK(ccell->GetBitmap()) ||
2646                    wxGDI_IS_OK(ccell->GetFgCol()) ||
2647                    wxGDI_IS_OK(ccell->GetBgCol()) )
2648                )
2649                 cell = ccell;
2650         }
2651     }
2652 
2653     int preDrawFlags = flags;
2654 
2655     if ( cell )
2656     {
2657         if ( propertyGrid->GetInternalFlags() & wxPG_FL_CELL_OVERRIDES_SEL )
2658             preDrawFlags = preDrawFlags & ~(Selected);
2659 
2660         // Always use check box editor's DrawValue() function, if present
2661         editor = property->GetColumnEditor(column);
2662         if ( editor && !editor->IsKindOf(CLASSINFO(wxPGCheckBoxEditor)) )
2663             editor = NULL;
2664 
2665         imageWidth = PreDrawCell( dc, rect, *cell, preDrawFlags );
2666         text = cell->GetText();
2667         if ( text == gs_noCellText )
2668         {
2669             if ( column == 0 )
2670                 text = property->GetLabel();
2671             else if ( column == 1 )
2672                 text = property->GetValueString();
2673             else
2674                 text = wxEmptyString;
2675         }
2676     }
2677     else if ( column == 0 )
2678     {
2679         // Caption
2680         DrawText( dc, rect, 0, property->GetLabel() );
2681     }
2682     else if ( column == 1 )
2683     {
2684         editor = property->GetColumnEditor(column);
2685         if ( !isUnspecified )
2686         {
2687             // Regular property value
2688 
2689             wxSize imageSize = propertyGrid->GetImageSize(property, item);
2690 
2691             wxPGPaintData paintdata;
2692             paintdata.m_parent = propertyGrid;
2693             paintdata.m_choiceItem = item;
2694 
2695             if ( imageSize.x > 0 )
2696             {
2697                 wxRect imageRect(rect.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
2698                                  rect.y+wxPG_CUSTOM_IMAGE_SPACINGY,
2699                                  wxPG_CUSTOM_IMAGE_WIDTH,
2700                                  rect.height-(wxPG_CUSTOM_IMAGE_SPACINGY*2));
2701 
2702                 /*if ( imageSize.x == wxPG_FULL_CUSTOM_PAINT_WIDTH )
2703                 {
2704                     imageRect.width = m_width - imageRect.x;
2705                 }*/
2706 
2707                 dc.SetPen( wxPen(propertyGrid->GetCellTextColour(), 1, wxSOLID) );
2708 
2709                 paintdata.m_drawnWidth = imageSize.x;
2710                 paintdata.m_drawnHeight = imageSize.y;
2711 
2712                 if ( !isUnspecified )
2713                 {
2714                     property->OnCustomPaint( dc, imageRect, paintdata );
2715                 }
2716                 else
2717                 {
2718                     dc.SetBrush(*wxWHITE_BRUSH);
2719                     dc.DrawRectangle(imageRect);
2720                 }
2721 
2722                 imageWidth = paintdata.m_drawnWidth;
2723             }
2724 
2725             text = property->GetValueString();
2726 
2727             // Add units string?
2728             if ( propertyGrid->GetColumnCount() <= 2 )
2729             {
2730                 wxString unitsString = property->GetAttribute(wxPGGlobalVars->m_strUnits, wxEmptyString);
2731                 if ( unitsString.length() )
2732                     text = wxString::Format(wxT("%s %s"), text.c_str(), unitsString.c_str() );
2733             }
2734         }
2735 
2736         if ( text.length() == 0 )
2737         {
2738             // Try to show inline help if no text
2739             wxVariant vInlineHelp = property->GetAttribute(wxPGGlobalVars->m_strInlineHelp);
2740             if ( !vInlineHelp.IsNull() )
2741             {
2742                 text = vInlineHelp.GetString();
2743                 dc.SetTextForeground(propertyGrid->GetCellDisabledTextColour());
2744 
2745                 // Must make the editor NULL to override it's own rendering
2746                 // code.
2747                 editor = NULL;
2748             }
2749         }
2750     }
2751     else if ( column == 2 )
2752     {
2753         // Add units string?
2754         if ( !text.length() )
2755             text = property->GetAttribute(wxPGGlobalVars->m_strUnits, wxEmptyString);
2756     }
2757 
2758     int imageOffset = property->GetImageOffset(imageWidth);
2759 
2760     DrawEditorValue( dc, rect, imageOffset, text, property, editor );
2761 
2762     // active caption gets nice dotted rectangle
2763     if ( property->IsCategory() /*&& column == 0*/ )
2764     {
2765         if ( flags & Selected )
2766         {
2767             if ( imageOffset > 0 )
2768             {
2769                 imageOffset -= DEFAULT_IMAGE_OFFSET_INCREMENT;
2770                 imageOffset += wxCC_CUSTOM_IMAGE_MARGIN2 + 4;
2771             }
2772 
2773             DrawCaptionSelectionRect( dc,
2774                                       rect.x+wxPG_XBEFORETEXT-wxPG_CAPRECTXMARGIN+imageOffset,
2775                                       rect.y-wxPG_CAPRECTYMARGIN+1,
2776                                       ((wxPropertyCategory*)property)->GetTextExtent(propertyGrid,
2777                                                                                      propertyGrid->GetCaptionFont())
2778                                       +(wxPG_CAPRECTXMARGIN*2),
2779                                       propertyGrid->GetFontHeight()+(wxPG_CAPRECTYMARGIN*2) );
2780         }
2781     }
2782 
2783     if ( cell )
2784         PostDrawCell(dc, propertyGrid, *cell, preDrawFlags);
2785 }
2786 
GetImageSize(const wxPGProperty * property,int column,int item) const2787 wxSize wxPGDefaultRenderer::GetImageSize( const wxPGProperty* property,
2788                                           int column,
2789                                           int item ) const
2790 {
2791     if ( property && column == 1 )
2792     {
2793         if ( item == -1 )
2794         {
2795             wxBitmap* bmp = property->GetValueImage();
2796 
2797             if ( bmp && bmp->Ok() )
2798                 return wxSize(bmp->GetWidth(),bmp->GetHeight());
2799         }
2800     }
2801     return wxSize(0,0);
2802 }
2803 
2804 // -----------------------------------------------------------------------
2805 // wxPGCell
2806 // -----------------------------------------------------------------------
2807 
wxPGCell()2808 wxPGCell::wxPGCell()
2809 {
2810 }
2811 
HasText() const2812 bool wxPGCell::HasText() const
2813 {
2814     if ( m_text != gs_noCellText )
2815         return true;
2816     return false;
2817 }
2818 
wxPGCell(const wxString & text,const wxBitmap & bitmap,const wxColour & fgCol,const wxColour & bgCol)2819 wxPGCell::wxPGCell( const wxString& text,
2820                     const wxBitmap& bitmap,
2821                     const wxColour& fgCol,
2822                     const wxColour& bgCol )
2823     : m_bitmap(bitmap), m_fgCol(fgCol), m_bgCol(bgCol)
2824 {
2825 #ifndef __WXPYTHON__
2826     if ( text != wxString_wxPG_LABEL )
2827 #else
2828     if ( (&text != ((wxString*)NULL)) && text != wxT("_LABEL_AS_NAME") )
2829 #endif
2830     {
2831         wxASSERT_MSG( m_text != gs_noCellText,
2832             wxT("\"@!\" is not an allowed as a cell text.") );
2833         m_text = text;
2834     }
2835     else
2836     {
2837         m_text = gs_noCellText;
2838     }
2839 }
2840 
Assign(const wxPGCell & cell)2841 void wxPGCell::Assign(const wxPGCell& cell)
2842 {
2843     if ( cell.HasText() )
2844         SetText(cell.GetText());
2845 
2846     const wxBitmap& bmp = cell.GetBitmap();
2847     if ( wxGDI_IS_OK(bmp) )
2848         SetBitmap(bmp);
2849 
2850     const wxColour& fgCol = cell.GetFgCol();
2851     if ( wxGDI_IS_OK(fgCol) )
2852         SetFgCol(fgCol);
2853 
2854     const wxColour& bgCol = cell.GetBgCol();
2855     if ( wxGDI_IS_OK(bgCol) )
2856         SetBgCol(bgCol);
2857 
2858     const wxFont& font = cell.GetFont();
2859     if ( wxGDI_IS_OK(font) )
2860         SetFont(font);
2861 }
2862 
2863 // -----------------------------------------------------------------------
2864 // wxPGBrush
2865 // -----------------------------------------------------------------------
2866 
2867 //
2868 // This class is a wxBrush derivative used in the background colour
2869 // brush cache. It adds wxPG-type colour-in-long to the class.
2870 // JMS: Yes I know wxBrush doesn't actually hold the value (refcounted
2871 //   object does), but this is simpler implementation and equally
2872 //   effective.
2873 //
2874 
2875 class wxPGBrush : public wxBrush
2876 {
2877 public:
2878     wxPGBrush( const wxColour& colour );
2879     wxPGBrush();
~wxPGBrush()2880     ~wxPGBrush() override { }
2881     void SetColour2( const wxColour& colour );
GetColourAsLong() const2882     inline long GetColourAsLong() const { return m_colAsLong; }
2883 private:
2884     long    m_colAsLong;
2885 };
2886 
2887 
SetColour2(const wxColour & colour)2888 void wxPGBrush::SetColour2( const wxColour& colour )
2889 {
2890     wxBrush::SetColour(colour);
2891     m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
2892 }
2893 
2894 
wxPGBrush()2895 wxPGBrush::wxPGBrush() : wxBrush()
2896 {
2897     m_colAsLong = 0;
2898 }
2899 
2900 
wxPGBrush(const wxColour & colour)2901 wxPGBrush::wxPGBrush( const wxColour& colour ) : wxBrush(colour)
2902 {
2903     m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
2904 }
2905 
2906 
2907 // -----------------------------------------------------------------------
2908 // wxPGColour
2909 // -----------------------------------------------------------------------
2910 
2911 //
2912 // Same as wxPGBrush, but for wxColour instead.
2913 //
2914 
2915 class wxPGColour : public wxColour
2916 {
2917 public:
2918     wxPGColour( const wxColour& colour );
2919     wxPGColour();
~wxPGColour()2920     ~wxPGColour() override { }
2921     void SetColour2( const wxColour& colour );
GetColourAsLong() const2922     inline long GetColourAsLong() const { return m_colAsLong; }
2923 private:
2924     long    m_colAsLong;
2925 };
2926 
2927 
SetColour2(const wxColour & colour)2928 void wxPGColour::SetColour2( const wxColour& colour )
2929 {
2930     *this = colour;
2931     m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
2932 }
2933 
2934 
wxPGColour()2935 wxPGColour::wxPGColour() : wxColour()
2936 {
2937     m_colAsLong = 0;
2938 }
2939 
2940 
wxPGColour(const wxColour & colour)2941 wxPGColour::wxPGColour( const wxColour& colour ) : wxColour(colour)
2942 {
2943     m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
2944 }
2945 
2946 // -----------------------------------------------------------------------
2947 // wxPGCanvas
2948 // -----------------------------------------------------------------------
2949 
2950 //
2951 // wxPGCanvas acts as a graphics sub-window of the
2952 // wxScrolledWindow that wxPropertyGrid is.
2953 //
2954 class wxPGCanvas : public wxPanel
2955 {
2956 public:
wxPGCanvas()2957     wxPGCanvas() : wxPanel()
2958     {
2959     }
~wxPGCanvas()2960     ~wxPGCanvas() override { }
2961 
2962 protected:
OnMouseMove(wxMouseEvent & event)2963     void OnMouseMove( wxMouseEvent &event )
2964     {
2965         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
2966         pg->OnMouseMove( event );
2967     }
2968 
OnMouseClick(wxMouseEvent & event)2969     void OnMouseClick( wxMouseEvent &event )
2970     {
2971         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
2972         pg->OnMouseClick( event );
2973     }
2974 
OnMouseUp(wxMouseEvent & event)2975     void OnMouseUp( wxMouseEvent &event )
2976     {
2977         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
2978         pg->OnMouseUp( event );
2979     }
2980 
OnMouseRightClick(wxMouseEvent & event)2981     void OnMouseRightClick( wxMouseEvent &event )
2982     {
2983         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
2984         pg->OnMouseRightClick( event );
2985     }
2986 
OnMouseDoubleClick(wxMouseEvent & event)2987     void OnMouseDoubleClick( wxMouseEvent &event )
2988     {
2989         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
2990         pg->OnMouseDoubleClick( event );
2991     }
2992 
OnKey(wxKeyEvent & event)2993     void OnKey( wxKeyEvent& event )
2994     {
2995         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
2996         pg->OnKey( event );
2997 /* C::B begin */
2998         int id = event.GetId();
2999         event.SetId(pg->GetId());
3000         pg->ProcessEvent(event);
3001         event.SetId(id);
3002 /* C::B end */
3003     }
3004 
OnKeyUp(wxKeyEvent & event)3005     void OnKeyUp( wxKeyEvent& event )
3006     {
3007         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
3008         pg->OnKeyUp( event );
3009 /* C::B begin */
3010         int id = event.GetId();
3011         event.SetId(pg->GetId());
3012         pg->ProcessEvent(event);
3013         event.SetId(id);
3014 /* C::B end */
3015     }
3016 
OnNavigationKey(wxNavigationKeyEvent & event)3017     void OnNavigationKey( wxNavigationKeyEvent& event )
3018     {
3019         wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
3020         pg->OnNavigationKey( event );
3021 /* C::B begin */
3022         int id = event.GetId();
3023         event.SetId(pg->GetId());
3024         pg->ProcessEvent(event);
3025         event.SetId(id);
3026 /* C::B end */
3027     }
3028 
3029     void OnPaint( wxPaintEvent& event );
3030 
3031 private:
3032     DECLARE_EVENT_TABLE()
3033 };
3034 
3035 
BEGIN_EVENT_TABLE(wxPGCanvas,wxPanel)3036 BEGIN_EVENT_TABLE(wxPGCanvas, wxPanel)
3037     EVT_MOTION(wxPGCanvas::OnMouseMove)
3038     EVT_PAINT(wxPGCanvas::OnPaint)
3039     EVT_LEFT_DOWN(wxPGCanvas::OnMouseClick)
3040     EVT_LEFT_UP(wxPGCanvas::OnMouseUp)
3041     EVT_RIGHT_UP(wxPGCanvas::OnMouseRightClick)
3042     EVT_LEFT_DCLICK(wxPGCanvas::OnMouseDoubleClick)
3043     EVT_KEY_DOWN(wxPGCanvas::OnKey)
3044     EVT_KEY_UP(wxPGCanvas::OnKeyUp)
3045     EVT_CHAR(wxPGCanvas::OnKey)
3046     EVT_NAVIGATION_KEY(wxPGCanvas::OnNavigationKey)
3047 END_EVENT_TABLE()
3048 
3049 
3050 void wxPGCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
3051 {
3052     wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
3053     wxASSERT( pg->IsKindOf(CLASSINFO(wxPropertyGrid)) );
3054 
3055     wxPaintDC dc(this);
3056 
3057     // Don't paint after destruction has begun
3058     if ( !(pg->GetInternalFlags() & wxPG_FL_INITIALIZED) )
3059         return;
3060 
3061     // Update everything inside the box
3062     wxRect r = GetUpdateRegion().GetBox();
3063 
3064     // FIXME: This is just a workaround for a bug that causes splitters not
3065     //        to paint when other windows are being dragged over the grid.
3066     r.x = 0;
3067     r.width = GetClientSize().x;
3068 
3069     // Repaint this rectangle
3070     pg->DrawItems( dc, r.y, r.y + r.height, &r );
3071 
3072     // We assume that the size set when grid is shown
3073     // is what is desired.
3074     pg->SetInternalFlag(wxPG_FL_GOOD_SIZE_SET);
3075 }
3076 
3077 // -----------------------------------------------------------------------
3078 // wxPropertyGrid
3079 // -----------------------------------------------------------------------
3080 
IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid,wxScrolledWindow)3081 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxScrolledWindow)
3082 
3083 BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
3084   EVT_IDLE(wxPropertyGrid::OnIdle)
3085   EVT_MOTION(wxPropertyGrid::OnMouseMoveBottom)
3086   EVT_PAINT(wxPropertyGrid::OnPaint)
3087   EVT_SIZE(wxPropertyGrid::OnResize)
3088   EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry)
3089   EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry)
3090   EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange)
3091   EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent)
3092   EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent)
3093   EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent)
3094   EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent)
3095   EVT_TEXT_ENTER(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
3096   EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged)
3097 END_EVENT_TABLE()
3098 
3099 
3100 // -----------------------------------------------------------------------
3101 
3102 wxPropertyGrid::wxPropertyGrid()
3103     : wxScrolledWindow()
3104 {
3105     Init1();
3106 }
3107 
3108 // -----------------------------------------------------------------------
3109 
wxPropertyGrid(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)3110 wxPropertyGrid::wxPropertyGrid( wxWindow *parent,
3111                                 wxWindowID id,
3112                                 const wxPoint& pos,
3113                                 const wxSize& size,
3114                                 long style,
3115                                 const wxString& name )
3116     : wxScrolledWindow()
3117 {
3118     Init1();
3119     Create(parent,id,pos,size,style,name);
3120 }
3121 
3122 // -----------------------------------------------------------------------
3123 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)3124 bool wxPropertyGrid::Create( wxWindow *parent,
3125                              wxWindowID id,
3126                              const wxPoint& pos,
3127                              const wxSize& size,
3128                              long style,
3129                              const wxString& name )
3130 {
3131     // Use a native border if border not specified
3132     if ( !(style&wxBORDER_MASK) || (style & wxBORDER_THEME) )
3133     {
3134 #ifdef __WXMSW__
3135         // Themed border looks bad on Windows due to white inner border, so
3136         // draw internally in this case.
3137         // Unfortunately we can't easily draw our own border, so revert to
3138         // themed drawing.
3139     #ifdef __WXMSW__
3140         style |= GetThemedBorderStyle();
3141     #else
3142         style |= wxBORDER_SIMPLE;
3143     #endif
3144 
3145 #else
3146         style |= wxBORDER_SUNKEN;
3147 #endif
3148     }
3149 
3150     style |= wxVSCROLL;
3151 
3152 #ifdef __WXMSW__
3153     // This prevents crash under Win2K, but still
3154     // enables keyboard navigation
3155     if ( style & wxTAB_TRAVERSAL )
3156     {
3157         style &= ~(wxTAB_TRAVERSAL);
3158         style |= wxWANTS_CHARS;
3159     }
3160 #else
3161     if ( style & wxTAB_TRAVERSAL )
3162         style |= wxWANTS_CHARS;
3163 #endif
3164 
3165     wxScrolledWindow::Create(parent,id,pos,size,style,name);
3166 
3167     Init2();
3168 
3169     return true;
3170 }
3171 
3172 // -----------------------------------------------------------------------
3173 
3174 //
3175 // Initialize values to defaults
3176 //
Init1()3177 void wxPropertyGrid::Init1()
3178 {
3179 #if !wxPG_USE_WXMODULE
3180     if ( !wxPGGlobalVars )
3181         wxPGGlobalVars = new wxPGGlobalVarsClass();
3182 #endif
3183 
3184     // Register editor classes, if necessary.
3185     if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
3186         RegisterDefaultEditors();
3187 
3188     m_iFlags = 0;
3189     m_pState = (wxPropertyGridState*) NULL;
3190     m_wndEditor = m_wndEditor2 = (wxWindow*) NULL;
3191     m_selColumn = 1;
3192     m_colHover = 1;
3193     m_propHover = (wxPGProperty*) NULL;
3194     m_labelEditor = NULL;
3195     m_labelEditorProperty = NULL;
3196     m_eventObject = this;
3197     m_curFocused = (wxWindow*) NULL;
3198     m_processedEvent = NULL;
3199     m_sortFunction = NULL;
3200     m_inDoPropertyChanged = 0;
3201     m_inCommitChangesFromEditor = 0;
3202     m_inDoSelectProperty = false;
3203     m_inOnValidationFailure = false;
3204     m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT;
3205     m_dragStatus = 0;
3206     m_mouseSide = 16;
3207     m_editorFocused = 0;
3208 
3209     // Set up default unspecified value 'colour'
3210     m_unspecifiedAppearance.SetFgCol(*wxLIGHT_GREY);
3211 
3212     // Set default keys
3213     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT );
3214     AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
3215     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_LEFT );
3216     AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_UP );
3217     AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY, WXK_RIGHT);
3218     AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY, WXK_LEFT);
3219     AddActionTrigger( wxPG_ACTION_CANCEL_EDIT, WXK_ESCAPE );
3220     AddActionTrigger( wxPG_ACTION_SELECT_ALL, 'A', wxMOD_CONTROL );
3221 
3222     m_coloursCustomized = 0;
3223     m_frozen = 0;
3224 
3225     m_canvas = NULL;
3226 
3227 #if wxPG_DOUBLE_BUFFER
3228     m_doubleBuffer = (wxBitmap*) NULL;
3229 #endif
3230 
3231     m_windowsToDelete = NULL;
3232 
3233 #ifndef wxPG_ICON_WIDTH
3234 	m_expandbmp = NULL;
3235 	m_collbmp = NULL;
3236 	m_iconWidth = 11;
3237 	m_iconHeight = 11;
3238 #else
3239     m_iconWidth = wxPG_ICON_WIDTH;
3240 #endif
3241 
3242     m_prevVY = -1;
3243 
3244     m_gutterWidth = wxPG_GUTTER_MIN;
3245     m_subgroup_extramargin = 10;
3246 
3247     m_lineHeight = 0;
3248 
3249     m_width = m_height = 0;
3250 
3251     SetButtonShortcut(0);
3252 
3253     m_keyComboConsumed = 0;
3254 
3255     m_commonValues.push_back(new wxPGCommonValue(_("Unspecified"), wxPGGlobalVars->m_defaultRenderer) );
3256     m_cvUnspecified = 0;
3257 
3258     m_chgInfo_changedProperty = NULL;
3259 }
3260 
3261 // -----------------------------------------------------------------------
3262 
3263 //
3264 // Initialize after parent etc. set
3265 //
Init2()3266 void wxPropertyGrid::Init2()
3267 {
3268     wxASSERT( !(m_iFlags & wxPG_FL_INITIALIZED ) );
3269 
3270 #ifdef __WXMAC__
3271    // Smaller controls on Mac
3272    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
3273 #endif
3274 
3275     // Now create state, if one didn't exist already
3276     // (wxPropertyGridManager might have created it for us).
3277     if ( !m_pState )
3278     {
3279         m_pState = CreateState();
3280         m_pState->m_pPropGrid = this;
3281         m_iFlags |= wxPG_FL_CREATEDSTATE;
3282     }
3283 
3284     if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) )
3285         m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
3286 
3287     if ( m_windowStyle & wxPG_HIDE_CATEGORIES )
3288     {
3289         m_pState->InitNonCatMode();
3290 
3291         m_pState->m_properties = m_pState->m_abcArray;
3292     }
3293 
3294     GetClientSize(&m_width,&m_height);
3295 
3296 #ifndef wxPG_ICON_WIDTH
3297     // create two bitmap nodes for drawing
3298 	m_expandbmp = new wxBitmap(expand_xpm);
3299 	m_collbmp = new wxBitmap(collapse_xpm);
3300 
3301 	// calculate average font height for bitmap centering
3302 
3303 	m_iconWidth = m_expandbmp->GetWidth();
3304 	m_iconHeight = m_expandbmp->GetHeight();
3305 #endif
3306 
3307     m_curcursor = wxCURSOR_ARROW;
3308     m_cursorSizeWE = new wxCursor( wxCURSOR_SIZEWE );
3309 
3310 	// adjust bitmap icon y position so they are centered
3311     m_vspacing = wxPG_DEFAULT_VSPACING;
3312 
3313     //SetFont(*wxNORMAL_FONT);
3314     CalculateFontAndBitmapStuff(m_vspacing);
3315 
3316     // Add base brush item
3317     m_arrBgBrushes.Add((void*)new wxPGBrush());
3318 
3319     // Add base colour items
3320     m_arrFgCols.Add((void*)new wxPGColour());
3321     m_arrFgCols.Add((void*)new wxPGColour());
3322 
3323     RegainColours();
3324 
3325     // This helps with flicker
3326     SetBackgroundStyle( wxBG_STYLE_CUSTOM );
3327 
3328     // Hook the TLW
3329     m_tlp = NULL;
3330     m_tlpClosed = NULL;
3331     m_tlpClosedTime = 0;
3332     OnTLPChanging(::wxGetTopLevelParent(this));
3333 
3334 	// set virtual size to this window size
3335     wxSize wndsize = GetSize();
3336 	SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth());
3337 
3338     m_timeCreated = ::wxGetLocalTimeMillis();
3339 
3340     m_canvas = new wxPGCanvas();
3341     m_canvas->Create(this, 1, wxPoint(0, 0), GetClientSize(),
3342                      (GetWindowStyle() & wxTAB_TRAVERSAL) |
3343                                          wxWANTS_CHARS |
3344                                          wxCLIP_CHILDREN);
3345     m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM );
3346 
3347     wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
3348     sizer->Add(m_canvas, 1, wxEXPAND, 0);
3349     SetSizer(sizer);
3350     SetScrollRate(wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT);
3351 
3352     m_iFlags |= wxPG_FL_INITIALIZED;
3353 
3354     m_ncWidth = wndsize.GetWidth();
3355 
3356     // Need to call OnResize handler or size given in constructor/Create
3357     // will never work.
3358     wxSizeEvent sizeEvent(wndsize,0);
3359     OnResize(sizeEvent);
3360 }
3361 
3362 // -----------------------------------------------------------------------
3363 
~wxPropertyGrid()3364 wxPropertyGrid::~wxPropertyGrid()
3365 {
3366     size_t i;
3367 
3368     if ( m_processedEvent )
3369     {
3370         // All right... we are being deleted while wxPropertyGrid event
3371         // is being sent. Make sure that event propagates as little
3372         // as possible (although usually this is not enough to prevent
3373         // a crash).
3374         m_processedEvent->Skip(false);
3375         m_processedEvent->StopPropagation();
3376 
3377         // Let's use wxMessageBox to make the message appear more
3378         // reliably (and *before* the crash can happen).
3379         ::wxMessageBox(wxT("wxPropertyGrid was being destroyed in an event ")
3380                        wxT("generated by it. This usually leads to a crash ")
3381                        wxT("so it is recommended to destroy the control ")
3382                        wxT("at idle time instead."));
3383     }
3384 
3385     DoSelectProperty(NULL, wxPG_SEL_NOVALIDATE|wxPG_SEL_DONT_SEND_EVENT);
3386 
3387     // This should do prevent things from going too badly wrong
3388     m_iFlags &= ~(wxPG_FL_INITIALIZED);
3389 
3390     if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
3391         m_canvas->ReleaseMouse();
3392 
3393     // Call with NULL to disconnect event handling
3394     if ( !(GetExtraStyle() & wxPG_EX_DISABLE_TLP_TRACKING) )
3395     {
3396         OnTLPChanging(NULL);
3397 
3398     #ifdef __WXDEBUG__
3399         if ( IsEditorsValueModified() )
3400             ::wxMessageBox(wxT("Most recent change in property editor ")
3401                            wxT("was lost!!!\n\n(if you don't want this ")
3402                            wxT("to happen, close your frames and ")
3403                            wxT("dialogs using Close(false).)"),
3404                            wxT("wxPropertyGrid Debug Warning") );
3405     #endif
3406     }
3407 
3408 #if wxPG_DOUBLE_BUFFER
3409     if ( m_doubleBuffer )
3410         delete m_doubleBuffer;
3411 #endif
3412 
3413     delete m_windowsToDelete;
3414 
3415     if ( m_iFlags & wxPG_FL_CREATEDSTATE )
3416         delete m_pState;
3417 
3418     delete m_cursorSizeWE;
3419 
3420 #ifndef wxPG_ICON_WIDTH
3421 	delete m_expandbmp;
3422 	delete m_collbmp;
3423 #endif
3424 
3425     // Delete cached text colours.
3426     for ( i=0; i<m_arrFgCols.GetCount(); i++ )
3427     {
3428         delete (wxPGColour*)m_arrFgCols.Item(i);
3429     }
3430 
3431     // Delete cached brushes.
3432     for ( i=0; i<m_arrBgBrushes.GetCount(); i++ )
3433     {
3434         delete (wxPGBrush*)m_arrBgBrushes.Item(i);
3435     }
3436 
3437     // Delete common value records
3438     for ( i=0; i<m_commonValues.size(); i++ )
3439     {
3440         delete GetCommonValue(i);
3441     }
3442 }
3443 
3444 // -----------------------------------------------------------------------
3445 
Destroy()3446 bool wxPropertyGrid::Destroy()
3447 {
3448     if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
3449         m_canvas->ReleaseMouse();
3450 
3451     return wxScrolledWindow::Destroy();
3452 }
3453 
3454 // -----------------------------------------------------------------------
3455 
CreateState() const3456 wxPropertyGridState* wxPropertyGrid::CreateState() const
3457 {
3458     return new wxPropertyGridState();
3459 }
3460 
3461 // -----------------------------------------------------------------------
3462 // wxPropertyGrid overridden wxWindow methods
3463 // -----------------------------------------------------------------------
3464 
SetWindowStyleFlag(long style)3465 void wxPropertyGrid::SetWindowStyleFlag( long style )
3466 {
3467     long old_style = m_windowStyle;
3468 
3469     if ( m_iFlags & wxPG_FL_INITIALIZED )
3470     {
3471         wxASSERT( m_pState );
3472 
3473         if ( !(style & wxPG_HIDE_CATEGORIES) && (old_style & wxPG_HIDE_CATEGORIES) )
3474         {
3475         // Enable categories
3476             EnableCategories( true );
3477         }
3478         else if ( (style & wxPG_HIDE_CATEGORIES) && !(old_style & wxPG_HIDE_CATEGORIES) )
3479         {
3480         // Disable categories
3481             EnableCategories( false );
3482         }
3483         if ( !(old_style & wxPG_AUTO_SORT) && (style & wxPG_AUTO_SORT) )
3484         {
3485             //
3486             // Autosort enabled
3487             //
3488             if ( !m_frozen )
3489                 PrepareAfterItemsAdded();
3490             else
3491                 m_pState->m_itemsAdded = 1;
3492         }
3493     #if wxPG_SUPPORT_TOOLTIPS
3494         if ( !(old_style & wxPG_TOOLTIPS) && (style & wxPG_TOOLTIPS) )
3495         {
3496             //
3497             // Tooltips enabled
3498             //
3499             /*
3500             wxToolTip* tooltip = new wxToolTip ( wxEmptyString );
3501             SetToolTip ( tooltip );
3502             tooltip->SetDelay ( wxPG_TOOLTIP_DELAY );
3503             */
3504         }
3505         else if ( (old_style & wxPG_TOOLTIPS) && !(style & wxPG_TOOLTIPS) )
3506         {
3507             //
3508             // Tooltips disabled
3509             //
3510             m_canvas->SetToolTip( (wxToolTip*) NULL );
3511         }
3512     #endif
3513     }
3514 
3515     wxScrolledWindow::SetWindowStyleFlag ( style );
3516 
3517     if ( m_iFlags & wxPG_FL_INITIALIZED )
3518     {
3519         if ( (old_style & wxPG_HIDE_MARGIN) != (style & wxPG_HIDE_MARGIN) )
3520         {
3521             CalculateFontAndBitmapStuff( m_vspacing );
3522             Refresh();
3523         }
3524     }
3525 }
3526 
3527 // -----------------------------------------------------------------------
3528 
Freeze()3529 void wxPropertyGrid::Freeze()
3530 {
3531     m_frozen++;
3532     wxScrolledWindow::Freeze();
3533 }
3534 
3535 // -----------------------------------------------------------------------
3536 
Thaw()3537 void wxPropertyGrid::Thaw()
3538 {
3539     m_frozen--;
3540     wxScrolledWindow::Thaw();
3541     RecalculateVirtualSize();
3542 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
3543     m_canvas->Refresh();
3544 #endif
3545 
3546     // Force property re-selection.
3547     // NB: We must copy the selection.
3548     wxArrayPGProperty selection = m_pState->m_selection;
3549     DoSetSelection(selection, wxPG_SEL_FORCE | wxPG_SEL_NONVISIBLE);
3550 }
3551 
3552 // -----------------------------------------------------------------------
3553 
DoAddToSelection(wxPGProperty * prop,int selFlags)3554 bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags )
3555 {
3556     wxCHECK( prop, false );
3557 
3558     if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) )
3559         return DoSelectProperty(prop, selFlags);
3560 
3561     wxArrayPGProperty& selection = m_pState->m_selection;
3562 
3563     if ( !selection.size() )
3564     {
3565         return DoSelectProperty(prop, selFlags);
3566     }
3567     else
3568     {
3569         // For categories, only one can be selected at a time
3570         if ( prop->IsCategory() || selection[0]->IsCategory() )
3571             return true;
3572 
3573         selection.push_back(prop);
3574 
3575         // TODO: Enable following line in wxWidgets 2.9
3576         //if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
3577         {
3578             SendEvent( wxEVT_PG_SELECTED, prop, NULL );
3579         }
3580 
3581         DrawItem(prop);
3582     }
3583 
3584     return true;
3585 }
3586 
3587 // -----------------------------------------------------------------------
3588 
DoRemoveFromSelection(wxPGProperty * prop,int selFlags)3589 bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags )
3590 {
3591     wxCHECK( prop, false );
3592     bool res;
3593 
3594     wxArrayPGProperty& selection = m_pState->m_selection;
3595     if ( selection.size() <= 1 )
3596     {
3597         res = DoSelectProperty(NULL, selFlags);
3598     }
3599     else
3600     {
3601         m_pState->DoRemoveFromSelection(prop);
3602         DrawItem(prop);
3603         res = true;
3604     }
3605 
3606     return res;
3607 }
3608 
3609 // -----------------------------------------------------------------------
3610 
DoSelectAndEdit(wxPGProperty * prop,unsigned int colIndex,unsigned int selFlags)3611 bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty* prop,
3612                                       unsigned int colIndex,
3613                                       unsigned int selFlags )
3614 {
3615     //
3616     // NB: Enable following if label editor background colour is
3617     //     ever changed to any other than m_colSelBack.
3618     //
3619     // We use this workaround to prevent visible flicker when editing
3620     // a cell. Atleast on wxMSW, there is a difficult to find
3621     // (and perhaps prevent) redraw somewhere between making property
3622     // selected and enabling label editing.
3623     //
3624     //wxColour prevColSelBack = m_colSelBack;
3625     //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
3626 
3627     bool res;
3628 
3629     if ( colIndex == 1 )
3630     {
3631         res = DoSelectProperty(prop, selFlags);
3632     }
3633     else
3634     {
3635         // send event
3636         DoClearSelection(false, wxPG_SEL_NO_REFRESH);
3637 
3638         if ( m_pState->m_editableColumns.Index(colIndex) == wxNOT_FOUND )
3639         {
3640             res = DoAddToSelection(prop, selFlags);
3641         }
3642         else
3643         {
3644             res = DoAddToSelection(prop, selFlags|wxPG_SEL_NO_REFRESH);
3645 
3646             DoBeginLabelEdit(colIndex, selFlags);
3647         }
3648     }
3649 
3650     //m_colSelBack = prevColSelBack;
3651     return res;
3652 }
3653 
3654 // -----------------------------------------------------------------------
3655 
AddToSelectionFromInputEvent(wxPGProperty * prop,unsigned int colIndex,wxMouseEvent * mouseEvent,int selFlags)3656 bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
3657                                                    unsigned int colIndex,
3658                                                    wxMouseEvent* mouseEvent,
3659                                                    int selFlags )
3660 {
3661     const wxArrayPGProperty& selection = GetSelectedProperties();
3662     bool alreadySelected = m_pState->DoIsPropertySelected(prop);
3663     bool res = true;
3664 
3665     // Set to 2 if also add all items in between
3666     int addToExistingSelection = 0;
3667 
3668     if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION )
3669     {
3670         if ( mouseEvent )
3671         {
3672             if ( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN ||
3673                  mouseEvent->GetEventType() == wxEVT_RIGHT_UP )
3674             {
3675                 // Allow right-click for context menu without
3676                 // disturbing the selection.
3677                 if ( GetSelectedProperties().size() <= 1 ||
3678                      !alreadySelected )
3679                     return DoSelectAndEdit(prop, colIndex, selFlags);
3680                 return true;
3681             }
3682             else
3683             {
3684                 if ( mouseEvent->ControlDown() )
3685                 {
3686                     addToExistingSelection = 1;
3687                 }
3688                 else if ( mouseEvent->ShiftDown() )
3689                 {
3690                     if ( selection.size() > 0 && !prop->IsCategory() )
3691                         addToExistingSelection = 2;
3692                     else
3693                         addToExistingSelection = 1;
3694                 }
3695             }
3696         }
3697     }
3698 
3699     if ( addToExistingSelection == 1 )
3700     {
3701         // Add/remove one
3702         if ( !alreadySelected )
3703         {
3704             res = DoAddToSelection(prop, selFlags);
3705         }
3706         else if ( GetSelectedProperties().size() > 1 )
3707         {
3708             res = DoRemoveFromSelection(prop, selFlags);
3709         }
3710     }
3711     else if ( addToExistingSelection == 2 )
3712     {
3713         // Add this, and all in between
3714 
3715         // Find top selected property
3716         wxPGProperty* topSelProp = selection[0];
3717         int topSelPropY = topSelProp->GetY();
3718         for ( unsigned int i=1; i<selection.size(); i++ )
3719         {
3720             wxPGProperty* p = selection[i];
3721             int y = p->GetY();
3722             if ( y < topSelPropY )
3723             {
3724                 topSelProp = p;
3725                 topSelPropY = y;
3726             }
3727         }
3728 
3729         wxPGProperty* startFrom;
3730         wxPGProperty* stopAt;
3731 
3732         if ( prop->GetY() <= topSelPropY )
3733         {
3734             // Property is above selection (or same)
3735             startFrom = prop;
3736             stopAt = topSelProp;
3737         }
3738         else
3739         {
3740             // Property is below selection
3741             startFrom = topSelProp;
3742             stopAt = prop;
3743         }
3744 
3745         // Iterate through properties in-between, and select them
3746         wxPropertyGridIterator it;
3747 
3748         for ( it = GetIterator(wxPG_ITERATE_VISIBLE, startFrom);
3749               !it.AtEnd();
3750               it++ )
3751         {
3752             wxPGProperty* p = *it;
3753 
3754             if ( !p->IsCategory() &&
3755                  !m_pState->DoIsPropertySelected(p) )
3756             {
3757                 DoAddToSelection(p, selFlags);
3758             }
3759 
3760             if ( p == stopAt )
3761                 break;
3762         }
3763     }
3764     else
3765     {
3766         res = DoSelectAndEdit(prop, colIndex, selFlags);
3767     }
3768 
3769     return res;
3770 }
3771 
3772 // -----------------------------------------------------------------------
3773 
DoSetSelection(const wxArrayPGProperty & newSelection,int selFlags)3774 void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection,
3775                                      int selFlags )
3776 {
3777     if ( newSelection.size() > 0 )
3778     {
3779         if ( !DoSelectProperty(newSelection[0], selFlags) )
3780             return;
3781     }
3782     else
3783     {
3784         DoClearSelection(false, selFlags);
3785     }
3786 
3787     for ( unsigned int i = 1; i < newSelection.size(); i++ )
3788     {
3789         DoAddToSelection(newSelection[i], selFlags);
3790     }
3791 
3792     Refresh();
3793 }
3794 
3795 // -----------------------------------------------------------------------
3796 
MakeColumnEditable(unsigned int column,bool editable)3797 void wxPropertyGrid::MakeColumnEditable( unsigned int column,
3798                                          bool editable )
3799 {
3800     wxASSERT( column != 1 );
3801 
3802     wxArrayInt& cols = m_pState->m_editableColumns;
3803 
3804     if ( editable )
3805     {
3806         cols.push_back(column);
3807     }
3808     else
3809     {
3810         for ( int i = cols.size() - 1; i > 0; i-- )
3811         {
3812             if ( cols[i] == (int)column )
3813                 cols.erase( cols.begin() + i );
3814         }
3815     }
3816 }
3817 
3818 // -----------------------------------------------------------------------
3819 
DoBeginLabelEdit(unsigned int colIndex,int selFlags)3820 void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex,
3821                                        int selFlags )
3822 {
3823     wxPGProperty* selected = GetSelection();
3824     wxCHECK_RET(selected, wxT("No property selected"));
3825     wxCHECK_RET(colIndex != 1, wxT("Do not use this for column 1"));
3826 
3827     if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
3828     {
3829         if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN,
3830                         selected, NULL, 0,
3831                         colIndex ) )
3832             return;
3833     }
3834 
3835     wxString text;
3836     wxPGCell* cell = selected->GetCell(colIndex);
3837     if ( !cell )
3838     {
3839         if ( colIndex == 0 )
3840             text = selected->GetLabel();
3841         else
3842             cell = selected->GetOrCreateCell(colIndex);
3843     }
3844 
3845     if ( cell )
3846         text = cell->GetText();
3847 
3848     // send event
3849     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
3850 
3851     m_selColumn = colIndex;
3852 
3853     wxRect r = GetEditorWidgetRect(selected, m_selColumn);
3854 
3855     wxWindow* ed = GenerateEditorTextCtrl(r.GetPosition(),
3856                                           r.GetSize(),
3857                                           text,
3858                                           NULL,
3859                                           wxTE_PROCESS_ENTER,
3860                                           0,
3861                                           colIndex);
3862 
3863     m_labelEditor = ed;
3864     wxTextCtrl* tc = GetLabelEditor();
3865 
3866     wxWindowID id = tc->GetId();
3867     tc->Connect(id, wxEVT_COMMAND_TEXT_ENTER,
3868         wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress),
3869         NULL, this);
3870     tc->Connect(id, wxEVT_KEY_DOWN,
3871         wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress),
3872         NULL, this);
3873 
3874     tc->SetFocus();
3875 
3876     m_labelEditorProperty = selected;
3877 
3878     DrawItem(selected);
3879 }
3880 
3881 // -----------------------------------------------------------------------
3882 
3883 void
OnLabelEditorEnterPress(wxCommandEvent & WXUNUSED (event))3884 wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent& WXUNUSED(event) )
3885 {
3886     DoEndLabelEdit(true);
3887 }
3888 
3889 // -----------------------------------------------------------------------
3890 
OnLabelEditorKeyPress(wxKeyEvent & event)3891 void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event )
3892 {
3893     int keycode = event.GetKeyCode();
3894 
3895     if ( keycode == WXK_ESCAPE )
3896     {
3897         DoEndLabelEdit(false);
3898     }
3899     else
3900     {
3901         // Do not block key presses, except left and right (to allow
3902         // normal wxTextCtrl usage)
3903         int secondAction;
3904         if ( KeyEventToActions(event, &secondAction) != wxPG_ACTION_INVALID &&
3905              keycode != WXK_LEFT && keycode != WXK_RIGHT)
3906             HandleKeyEvent(event);
3907         else
3908             event.Skip();
3909     }
3910 }
3911 
3912 // -----------------------------------------------------------------------
3913 
DoEndLabelEdit(bool commit,int selFlags)3914 void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags )
3915 {
3916     if ( !m_labelEditor )
3917         return;
3918 
3919     wxPGProperty* prop = m_labelEditorProperty;
3920     wxASSERT(prop);
3921 
3922     if ( commit )
3923     {
3924         if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
3925         {
3926             // wxPG_SEL_NOVALIDATE is passed correctly in selFlags
3927             if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING,
3928                             prop, NULL, selFlags,
3929                             m_selColumn ) )
3930                 return;
3931         }
3932 
3933         wxString text = GetLabelEditor()->GetValue();
3934         wxPGCell* cell = prop->GetCell(m_selColumn);
3935         if ( !cell )
3936         {
3937             if ( m_selColumn == 0 )
3938                 prop->SetLabel(text);
3939             else
3940                 cell = prop->GetOrCreateCell(m_selColumn);
3941         }
3942 
3943         if ( cell )
3944             cell->SetText(text);
3945     }
3946 
3947     m_selColumn = 1;
3948     int wasFocused = m_iFlags & wxPG_FL_FOCUSED;
3949 
3950     DestroyEditorWnd(m_labelEditor);
3951 
3952     m_labelEditor = NULL;
3953     m_labelEditorProperty = NULL;
3954 
3955     // Fix focus (needed at least on wxGTK)
3956     if ( wasFocused )
3957         SetFocusOnCanvas();
3958 
3959     DrawItem(prop);
3960 }
3961 
3962 // -----------------------------------------------------------------------
3963 
SetExtraStyle(long exStyle)3964 void wxPropertyGrid::SetExtraStyle( long exStyle )
3965 {
3966     if ( exStyle & wxPG_EX_DISABLE_TLP_TRACKING )
3967         OnTLPChanging(NULL);
3968     else
3969         OnTLPChanging(::wxGetTopLevelParent(this));
3970 
3971     if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING )
3972     {
3973 #if defined(__WXMSW__)
3974 
3975         /*
3976         // Don't use WS_EX_COMPOSITED just now.
3977         HWND hWnd;
3978 
3979         if ( m_iFlags & wxPG_FL_IN_MANAGER )
3980             hWnd = (HWND)GetParent()->GetHWND();
3981         else
3982             hWnd = (HWND)GetHWND();
3983 
3984         ::SetWindowLong( hWnd, GWL_EXSTYLE,
3985                          ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED );
3986         */
3987 
3988 //#elif defined(__WXGTK20__)
3989 #endif
3990         // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window
3991         // truly was double-buffered.
3992         if ( !wxPGIsWindowBuffered(this) )
3993         {
3994             exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING);
3995         }
3996         else
3997         {
3998         #if wxPG_DOUBLE_BUFFER
3999             delete m_doubleBuffer;
4000             m_doubleBuffer = NULL;
4001         #endif
4002         }
4003     }
4004 
4005     wxScrolledWindow::SetExtraStyle( exStyle );
4006 
4007     if ( exStyle & wxPG_EX_INIT_NOCAT )
4008         m_pState->InitNonCatMode();
4009 
4010     if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS )
4011         m_windowStyle |= wxPG_TOOLTIPS;
4012 
4013     // Set global style
4014     wxPGGlobalVars->m_extraStyle = exStyle;
4015 }
4016 
4017 // -----------------------------------------------------------------------
4018 
4019 // returns the best acceptable minimal size
DoGetBestSize() const4020 wxSize wxPropertyGrid::DoGetBestSize() const
4021 {
4022     int hei = 15;
4023     if ( m_lineHeight > hei )
4024         hei = m_lineHeight;
4025     wxSize sz = wxSize( 60, hei+40 );
4026 
4027     CacheBestSize(sz);
4028     return sz;
4029 }
4030 
4031 // -----------------------------------------------------------------------
4032 
OnTLPChanging(wxWindow * newTLP)4033 void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP )
4034 {
4035     if ( newTLP == m_tlp )
4036         return;
4037 
4038     wxLongLong currentTime = ::wxGetLocalTimeMillis();
4039 
4040     //
4041     // Parent changed so let's redetermine and re-hook the
4042     // correct top-level window.
4043     if ( m_tlp )
4044     {
4045         m_tlp->Disconnect( wxEVT_CLOSE_WINDOW,
4046                            wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
4047                            NULL, this );
4048         m_tlpClosed = m_tlp;
4049         m_tlpClosedTime = currentTime;
4050     }
4051 
4052     if ( newTLP )
4053     {
4054         // Only accept new tlp if same one was not just dismissed.
4055         if ( newTLP != m_tlpClosed ||
4056              m_tlpClosedTime+250 < currentTime )
4057         {
4058             newTLP->Connect( wxEVT_CLOSE_WINDOW,
4059                              wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
4060                              NULL, this );
4061             m_tlpClosed = NULL;
4062         }
4063         else
4064         {
4065             newTLP = NULL;
4066         }
4067     }
4068 
4069     m_tlp = newTLP;
4070 }
4071 
4072 // -----------------------------------------------------------------------
4073 
OnTLPClose(wxCloseEvent & event)4074 void wxPropertyGrid::OnTLPClose( wxCloseEvent& event )
4075 {
4076     // ClearSelection forces value validation/commit.
4077     if ( event.CanVeto() && !ClearSelection() )
4078     {
4079         event.Veto();
4080         return;
4081     }
4082 
4083     // Ok, it can close, set tlp pointer to NULL. Some other event
4084     // handler can of course veto the close, but our OnIdle() should
4085     // then be able to regain the tlp pointer.
4086     OnTLPChanging(NULL);
4087 
4088     event.Skip();
4089 }
4090 
4091 // -----------------------------------------------------------------------
4092 
Reparent(wxWindowBase * newParent)4093 bool wxPropertyGrid::Reparent( wxWindowBase *newParent )
4094 {
4095     OnTLPChanging((wxWindow*)newParent);
4096 
4097     bool res = wxScrolledWindow::Reparent(newParent);
4098 
4099     return res;
4100 }
4101 
4102 // -----------------------------------------------------------------------
4103 // wxPropertyGrid Font and Colour Methods
4104 // -----------------------------------------------------------------------
4105 
CalculateFontAndBitmapStuff(int vspacing)4106 void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
4107 {
4108 	int x = 0, y = 0;
4109 
4110     m_captionFont = GetFont();
4111 
4112 	GetTextExtent(wxT("jG"), &x, &y, 0, 0, &m_captionFont);
4113     m_subgroup_extramargin = x + (x/2);
4114 	m_fontHeight = y;
4115 
4116 #if wxPG_USE_RENDERER_NATIVE
4117     m_iconWidth = wxPG_ICON_WIDTH;
4118 #elif wxPG_ICON_WIDTH
4119     // scale icon
4120     m_iconWidth = (m_fontHeight * wxPG_ICON_WIDTH) / 13;
4121     if ( m_iconWidth < 5 ) m_iconWidth = 5;
4122     else if ( !(m_iconWidth & 0x01) ) m_iconWidth++; // must be odd
4123 
4124 #endif
4125 
4126     m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV;
4127     if ( m_gutterWidth < wxPG_GUTTER_MIN )
4128         m_gutterWidth = wxPG_GUTTER_MIN;
4129 
4130     int vdiv = 6;
4131     if ( vspacing <= 1 ) vdiv = 12;
4132     else if ( vspacing >= 3 ) vdiv = 3;
4133 
4134     m_spacingy = m_fontHeight / vdiv;
4135     if ( m_spacingy < wxPG_YSPACING_MIN )
4136         m_spacingy = wxPG_YSPACING_MIN;
4137 
4138     m_marginWidth = 0;
4139     if ( !(m_windowStyle & wxPG_HIDE_MARGIN) )
4140         m_marginWidth = m_gutterWidth*2 + m_iconWidth;
4141 
4142     m_captionFont.SetWeight(wxBOLD);
4143 	GetTextExtent(wxT("jG"), &x, &y, 0, 0, &m_captionFont);
4144 
4145     m_lineHeight = m_fontHeight+(2*m_spacingy)+1;
4146     m_visPropArray.SetCount((m_height/m_lineHeight)+10);
4147 
4148     // button spacing
4149     m_buttonSpacingY = (m_lineHeight - m_iconHeight) / 2;
4150     if ( m_buttonSpacingY < 0 ) m_buttonSpacingY = 0;
4151 
4152     if ( m_pState )
4153         m_pState->CalculateFontAndBitmapStuff(vspacing);
4154 
4155     if ( m_iFlags & wxPG_FL_INITIALIZED )
4156         RecalculateVirtualSize();
4157 
4158     InvalidateBestSize();
4159 }
4160 
4161 // -----------------------------------------------------------------------
4162 
OnSysColourChanged(wxSysColourChangedEvent & WXUNUSED (event))4163 void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent &WXUNUSED(event) )
4164 {
4165     RegainColours();
4166     Refresh();
4167 }
4168 
4169 // -----------------------------------------------------------------------
4170 
wxPGAdjustColour(const wxColour & src,int ra,int ga=1000,int ba=1000,bool forceDifferent=false)4171 static wxColour wxPGAdjustColour(const wxColour& src, int ra,
4172                                  int ga = 1000, int ba = 1000,
4173                                  bool forceDifferent = false)
4174 {
4175     if ( ga >= 1000 )
4176         ga = ra;
4177     if ( ba >= 1000 )
4178         ba = ra;
4179 
4180     // Recursion guard (allow 2 max)
4181     static int isinside = 0;
4182     isinside++;
4183     wxCHECK_MSG( isinside < 3,
4184                  *wxBLACK,
4185                  wxT("wxPGAdjustColour should not be recursively called more than once") );
4186 
4187     wxColour dst;
4188 
4189     int r = src.Red();
4190     int g = src.Green();
4191     int b = src.Blue();
4192     int r2 = r + ra;
4193     if ( r2>255 ) r2 = 255;
4194     else if ( r2<0) r2 = 0;
4195     int g2 = g + ga;
4196     if ( g2>255 ) g2 = 255;
4197     else if ( g2<0) g2 = 0;
4198     int b2 = b + ba;
4199     if ( b2>255 ) b2 = 255;
4200     else if ( b2<0) b2 = 0;
4201 
4202     // Make sure they are somewhat different
4203     if ( forceDifferent && (abs((r+g+b)-(r2+g2+b2)) < abs(ra/2)) )
4204         dst = wxPGAdjustColour(src,-(ra*2));
4205     else
4206         dst = wxColour(r2,g2,b2);
4207 
4208     // Recursion guard (allow 2 max)
4209     isinside--;
4210 
4211     return dst;
4212 }
4213 
4214 
wxPGGetColAvg(const wxColour & col)4215 static int wxPGGetColAvg( const wxColour& col )
4216 {
4217     return (col.Red() + col.Green() + col.Blue()) / 3;
4218 }
4219 
4220 
RegainColours()4221 void wxPropertyGrid::RegainColours()
4222 {
4223     wxColour def_bgcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
4224 
4225     if ( !(m_coloursCustomized & 0x0002) )
4226     {
4227         wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
4228 
4229         // Make sure colour is dark enough
4230     #ifdef __WXGTK__
4231         int colDec = wxPGGetColAvg(col) - 230;
4232     #else
4233         int colDec = wxPGGetColAvg(col) - 200;
4234     #endif
4235         if ( colDec > 0 )
4236             m_colCapBack = wxPGAdjustColour(col,-colDec);
4237         else
4238             m_colCapBack = col;
4239     }
4240 
4241     if ( !(m_coloursCustomized & 0x0001) )
4242         m_colMargin = m_colCapBack;
4243 
4244     if ( !(m_coloursCustomized & 0x0004) )
4245     {
4246     #ifdef __WXGTK__
4247         int colDec = -90;
4248     #else
4249         int colDec = -72;
4250     #endif
4251         wxColour capForeCol = wxPGAdjustColour(m_colCapBack,colDec,5000,5000,true);
4252         m_colCapFore = capForeCol;
4253 
4254         // Set the cached colour as well.
4255         ((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(capForeCol);
4256     }
4257 
4258     if ( !(m_coloursCustomized & 0x0008) )
4259     {
4260         wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
4261         m_colPropBack = bgCol;
4262 
4263         // Set the cached brush as well.
4264         ((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(bgCol);
4265     }
4266 
4267     if ( !(m_coloursCustomized & 0x0010) )
4268     {
4269         wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
4270         m_colPropFore = fgCol;
4271 
4272         // Set the cached colour as well.
4273         ((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(fgCol);
4274     }
4275 
4276     if ( !(m_coloursCustomized & 0x0020) )
4277         m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
4278 
4279     if ( !(m_coloursCustomized & 0x0040) )
4280         m_colSelFore = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
4281 
4282     if ( !(m_coloursCustomized & 0x0080) )
4283         m_colLine = m_colCapBack;
4284 
4285     if ( !(m_coloursCustomized & 0x0100) )
4286         m_colDisPropFore = m_colCapFore;
4287 
4288     m_colEmptySpace = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
4289 }
4290 
4291 // -----------------------------------------------------------------------
4292 
ResetColours()4293 void wxPropertyGrid::ResetColours()
4294 {
4295     m_coloursCustomized = 0;
4296 
4297     RegainColours();
4298 
4299     Refresh();
4300 }
4301 
4302 // -----------------------------------------------------------------------
4303 
SetFont(const wxFont & font)4304 bool wxPropertyGrid::SetFont( const wxFont& font )
4305 {
4306     // Must disable active editor.
4307     ClearSelection(false);
4308 
4309     // TODO: Following code is disabled with wxMac because
4310     //   it is reported to fail. I (JMS) cannot debug it
4311     //   personally right now.
4312     bool res = wxScrolledWindow::SetFont( font );
4313 
4314     // If this object has not been initialised properly (Create not called
4315     // yet), don't try to use wxClientDC. This can happen when SetVariant
4316     // is called from within the manager ctor, which triggers SetFont (and
4317     // subsequently the grid's SetFont) before the grid has been created.
4318 
4319     if ( res && GetParent() ) // may not have been Create()ed yet
4320     {
4321         CalculateFontAndBitmapStuff( m_vspacing );
4322 
4323         if ( m_pState )
4324             m_pState->CalculateFontAndBitmapStuff(m_vspacing);
4325 
4326         Refresh();
4327     }
4328 
4329     return res;
4330 }
4331 
4332 // -----------------------------------------------------------------------
4333 
SetLineColour(const wxColour & col)4334 void wxPropertyGrid::SetLineColour( const wxColour& col )
4335 {
4336     m_colLine = col;
4337     m_coloursCustomized |= 0x80;
4338     Refresh();
4339 }
4340 
4341 // -----------------------------------------------------------------------
4342 
SetMarginColour(const wxColour & col)4343 void wxPropertyGrid::SetMarginColour( const wxColour& col )
4344 {
4345     m_colMargin = col;
4346     m_coloursCustomized |= 0x01;
4347     Refresh();
4348 }
4349 
4350 // -----------------------------------------------------------------------
4351 
SetCellBackgroundColour(const wxColour & col)4352 void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col )
4353 {
4354     m_colPropBack = col;
4355     m_coloursCustomized |= 0x08;
4356 
4357     // Set the cached brush as well.
4358     ((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(col);
4359 
4360     Refresh();
4361 }
4362 
4363 // -----------------------------------------------------------------------
4364 
SetCellTextColour(const wxColour & col)4365 void wxPropertyGrid::SetCellTextColour( const wxColour& col )
4366 {
4367     m_colPropFore = col;
4368     m_coloursCustomized |= 0x10;
4369 
4370     // Set the cached colour as well.
4371     ((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(col);
4372 
4373     Refresh();
4374 }
4375 
4376 // -----------------------------------------------------------------------
4377 
SetEmptySpaceColour(const wxColour & col)4378 void wxPropertyGrid::SetEmptySpaceColour( const wxColour& col )
4379 {
4380     m_colEmptySpace = col;
4381 
4382     Refresh();
4383 }
4384 
4385 // -----------------------------------------------------------------------
4386 
SetCellDisabledTextColour(const wxColour & col)4387 void wxPropertyGrid::SetCellDisabledTextColour( const wxColour& col )
4388 {
4389     m_colDisPropFore = col;
4390     m_coloursCustomized |= 0x100;
4391     Refresh();
4392 }
4393 
4394 // -----------------------------------------------------------------------
4395 
SetSelectionBackground(const wxColour & col)4396 void wxPropertyGrid::SetSelectionBackground( const wxColour& col )
4397 {
4398     m_colSelBack = col;
4399     m_coloursCustomized |= 0x20;
4400     Refresh();
4401 }
4402 
4403 // -----------------------------------------------------------------------
4404 
SetSelectionForeground(const wxColour & col)4405 void wxPropertyGrid::SetSelectionForeground( const wxColour& col )
4406 {
4407     m_colSelFore = col;
4408     m_coloursCustomized |= 0x40;
4409     Refresh();
4410 }
4411 
4412 // -----------------------------------------------------------------------
4413 
SetCaptionBackgroundColour(const wxColour & col)4414 void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col )
4415 {
4416     m_colCapBack = col;
4417     m_coloursCustomized |= 0x02;
4418     Refresh();
4419 }
4420 
4421 // -----------------------------------------------------------------------
4422 
SetCaptionForegroundColour(const wxColour & col)4423 void wxPropertyGrid::SetCaptionForegroundColour( const wxColour& col )
4424 {
4425     m_colCapFore = col;
4426     m_coloursCustomized |= 0x04;
4427 
4428     // Set the cached colour as well.
4429     ((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(col);
4430 
4431     Refresh();
4432 }
4433 
4434 // -----------------------------------------------------------------------
4435 
SetBackgroundColourIndex(wxPGProperty * p,int index,int flags)4436 void wxPropertyGrid::SetBackgroundColourIndex( wxPGProperty* p,
4437                                                int index,
4438                                                int flags )
4439 {
4440     unsigned char ind = index;
4441 
4442     p->m_bgColIndex = ind;
4443 
4444     if ( flags & wxPG_RECURSE )
4445     {
4446         unsigned int i;
4447         for ( i=0; i<p->GetChildCount(); i++ )
4448             SetBackgroundColourIndex(p->Item(i),index);
4449     }
4450 }
4451 
4452 // -----------------------------------------------------------------------
4453 
SetPropertyBackgroundColour(wxPGPropArg id,const wxColour & colour,int flags)4454 void wxPropertyGrid::SetPropertyBackgroundColour( wxPGPropArg id,
4455                                                   const wxColour& colour,
4456                                                   int flags )
4457 {
4458     wxPG_PROP_ARG_CALL_PROLOG()
4459 
4460     size_t i;
4461     int colInd = -1;
4462 
4463     long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
4464 
4465     // As it is most likely that the previous colour is used, start comparison
4466     // from the end.
4467     for ( i=(m_arrBgBrushes.GetCount()-1); i>0; i-- )
4468     {
4469         if ( ((wxPGBrush*)m_arrBgBrushes.Item(i))->GetColourAsLong() ==
4470                 colAsLong )
4471         {
4472             colInd = i;
4473             break;
4474         }
4475     }
4476 
4477     if ( colInd < 0 )
4478     {
4479         colInd = m_arrBgBrushes.GetCount();
4480         wxCHECK_RET( colInd < 256,
4481                      wxT("wxPropertyGrid: Warning - Only 255 different ")
4482                      wxT("property background colours allowed.") );
4483         m_arrBgBrushes.Add( (void*)new wxPGBrush(colour) );
4484     }
4485 
4486     // Set indexes
4487     SetBackgroundColourIndex(p, colInd, flags);
4488 
4489     // If this was on a visible grid, then draw it.
4490     DrawItemAndChildren(p);
4491 }
4492 
4493 // -----------------------------------------------------------------------
4494 
GetPropertyBackgroundColour(wxPGPropArg id) const4495 wxColour wxPropertyGrid::GetPropertyBackgroundColour( wxPGPropArg id ) const
4496 {
4497     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxColour())
4498 
4499     if ( p->IsCategory() && p->m_bgColIndex == 0 )
4500         return GetCaptionBackgroundColour();
4501 
4502     return ((wxPGBrush*)m_arrBgBrushes.Item(p->m_bgColIndex))->GetColour();
4503 }
4504 
4505 // -----------------------------------------------------------------------
4506 
SetTextColourIndex(wxPGProperty * p,int index,int flags)4507 void wxPropertyGrid::SetTextColourIndex( wxPGProperty* p, int index,
4508                                          int flags )
4509 {
4510     unsigned char ind = index;
4511 
4512     p->m_fgColIndex = ind;
4513 
4514     if ( flags & wxPG_RECURSE )
4515     {
4516         unsigned int i;
4517         for ( i=0; i<p->GetChildCount(); i++ )
4518             SetTextColourIndex( p->Item(i), index, flags );
4519     }
4520 }
4521 
4522 // -----------------------------------------------------------------------
4523 
CacheColour(const wxColour & colour)4524 int wxPropertyGrid::CacheColour( const wxColour& colour )
4525 {
4526     unsigned int i;
4527     int colInd = -1;
4528 
4529     long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
4530 
4531     // As it is most likely that the previous colour is used, start comparison
4532     // from the end.
4533     for ( i=(m_arrFgCols.GetCount()-1); i>0; i-- )
4534     {
4535         if ( ((wxPGColour*)m_arrFgCols.Item(i))->GetColourAsLong() ==
4536                 colAsLong )
4537         {
4538             colInd = i;
4539             break;
4540         }
4541     }
4542 
4543     if ( colInd < 0 )
4544     {
4545         colInd = m_arrFgCols.GetCount();
4546         wxCHECK_MSG( colInd < 256, 0,
4547                      wxT("wxPropertyGrid: Warning - Only 255 ")
4548                      wxT("different property foreground colours allowed.") );
4549         m_arrFgCols.Add( (void*)new wxPGColour(colour) );
4550     }
4551 
4552     return colInd;
4553 }
4554 
4555 // -----------------------------------------------------------------------
4556 
SetPropertyTextColour(wxPGPropArg id,const wxColour & colour,int flags)4557 void wxPropertyGrid::SetPropertyTextColour( wxPGPropArg id,
4558                                             const wxColour& colour,
4559                                             int flags )
4560 {
4561     wxPG_PROP_ARG_CALL_PROLOG()
4562 
4563     // Set indexes
4564     SetTextColourIndex(p, CacheColour(colour), flags);
4565 
4566     // If this was on a visible grid, then draw it.
4567     DrawItemAndChildren(p);
4568 }
4569 
4570 // -----------------------------------------------------------------------
4571 
SetCaptionTextColour(wxPGPropArg id,const wxColour & colour)4572 void wxPropertyGrid::SetCaptionTextColour( wxPGPropArg id, const wxColour& colour )
4573 {
4574     wxPG_PROP_ARG_CALL_PROLOG()
4575 
4576     wxCHECK_RET( p->IsCategory(),
4577                  wxT("Only call SetCaptionTextColour for caption properties") );
4578 
4579     // Set indexes
4580     wxPropertyCategory* cat = (wxPropertyCategory*) p;
4581     cat->SetTextColIndex(CacheColour(colour));
4582 
4583     // If this was on a visible grid, then draw it.
4584     DrawItemAndChildren(p);
4585 }
4586 
4587 // -----------------------------------------------------------------------
4588 
GetPropertyTextColour(wxPGPropArg id) const4589 wxColour wxPropertyGrid::GetPropertyTextColour( wxPGPropArg id ) const
4590 {
4591     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxColour())
4592 
4593     return wxColour(*((wxPGColour*)m_arrFgCols.Item(p->m_fgColIndex)));
4594 }
4595 
4596 // -----------------------------------------------------------------------
4597 
SetPropertyColourToDefault(wxPGPropArg id)4598 void wxPropertyGrid::SetPropertyColourToDefault( wxPGPropArg id )
4599 {
4600     wxPG_PROP_ARG_CALL_PROLOG()
4601 
4602     SetBackgroundColourIndex( p, 0 );
4603     SetTextColourIndex( p, 0, wxPG_RECURSE );
4604 
4605     if ( p->IsCategory() )
4606     {
4607         wxPropertyCategory* cat = (wxPropertyCategory*) p;
4608         cat->SetTextColIndex(1);
4609     }
4610 }
4611 
4612 // -----------------------------------------------------------------------
4613 // wxPropertyGrid property adding and removal
4614 // -----------------------------------------------------------------------
4615 
RefreshGrid(wxPropertyGridState * state)4616 void wxPropertyGridInterface::RefreshGrid( wxPropertyGridState* state )
4617 {
4618     if ( !state )
4619         state = m_pState;
4620 
4621     wxPropertyGrid* grid = state->GetGrid();
4622     if ( grid->GetState() == state && !grid->IsFrozen() )
4623     {
4624         grid->Refresh();
4625     }
4626 }
4627 
4628 // -----------------------------------------------------------------------
4629 
Append(wxPGProperty * property)4630 wxPGProperty* wxPropertyGridInterface::Append( wxPGProperty* property )
4631 {
4632     return m_pState->DoAppend(property);
4633 }
4634 
4635 // -----------------------------------------------------------------------
4636 
AppendIn(wxPGPropArg id,wxPGProperty * newproperty)4637 wxPGProperty* wxPropertyGridInterface::AppendIn( wxPGPropArg id, wxPGProperty* newproperty )
4638 {
4639     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxNullProperty)
4640     wxPGProperty* pwc = (wxPGProperty*) p;
4641     wxPGProperty* retp = m_pState->DoInsert(pwc, pwc->GetChildCount(), newproperty);
4642     return retp;
4643 }
4644 
4645 // -----------------------------------------------------------------------
4646 
Insert(wxPGPropArg id,wxPGProperty * property)4647 wxPGProperty* wxPropertyGridInterface::Insert( wxPGPropArg id, wxPGProperty* property )
4648 {
4649     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxNullProperty)
4650     wxPGProperty* retp = m_pState->DoInsert(p->GetParent(), p->GetArrIndex(), property);
4651     RefreshGrid();
4652     return retp;
4653 }
4654 
4655 // -----------------------------------------------------------------------
4656 
Insert(wxPGPropArg id,int index,wxPGProperty * newproperty)4657 wxPGProperty* wxPropertyGridInterface::Insert( wxPGPropArg id, int index, wxPGProperty* newproperty )
4658 {
4659     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxNullProperty)
4660     wxPGProperty* retp = m_pState->DoInsert((wxPGProperty*)p,index,newproperty);
4661     RefreshGrid();
4662     return retp;
4663 }
4664 
4665 // -----------------------------------------------------------------------
4666 
DeleteProperty(wxPGPropArg id)4667 void wxPropertyGridInterface::DeleteProperty( wxPGPropArg id )
4668 {
4669     wxPG_PROP_ARG_CALL_PROLOG()
4670 
4671     wxPropertyGridState* state = p->GetParentState();
4672 
4673     state->DoDelete( p, true );
4674 
4675     RefreshGrid(state);
4676 }
4677 
4678 // -----------------------------------------------------------------------
4679 
RemoveProperty(wxPGPropArg id)4680 wxPGProperty* wxPropertyGridInterface::RemoveProperty( wxPGPropArg id )
4681 {
4682     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxNullProperty)
4683 
4684     wxCHECK( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE),
4685              wxNullProperty);
4686 
4687     wxPropertyGridState* state = p->GetParentState();
4688 
4689     state->DoDelete( p, false );
4690 
4691     RefreshGrid(state);
4692 
4693     return p;
4694 }
4695 
4696 // -----------------------------------------------------------------------
4697 
ReplaceProperty(wxPGPropArg id,wxPGProperty * property)4698 wxPGProperty* wxPropertyGridInterface::ReplaceProperty( wxPGPropArg id, wxPGProperty* property )
4699 {
4700     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxNullProperty)
4701 
4702     wxPGProperty* replaced = p;
4703     wxCHECK_MSG( replaced && property,
4704                  wxNullProperty,
4705                  wxT("NULL property") );
4706     wxCHECK_MSG( !replaced->IsCategory(),
4707                  wxNullProperty,
4708                  wxT("cannot replace this type of property") );
4709     wxCHECK_MSG( !m_pState->IsInNonCatMode(),
4710                  wxNullProperty,
4711                  wxT("cannot replace properties in alphabetic mode") );
4712 
4713     // Get address to the slot
4714     wxPGProperty* parent = replaced->GetParent();
4715     int ind = replaced->GetIndexInParent();
4716 
4717     wxPropertyGridState* state = replaced->GetParentState();
4718     DeleteProperty(replaced); // Must use generic Delete
4719     state->DoInsert(parent,ind,property);
4720 
4721     return property;
4722 }
4723 
4724 // -----------------------------------------------------------------------
4725 
PrepareAfterItemsAdded()4726 void wxPropertyGrid::PrepareAfterItemsAdded()
4727 {
4728     if ( m_pState && m_pState->m_itemsAdded )
4729     {
4730         m_pState->m_itemsAdded = 0;
4731 
4732         if ( m_windowStyle & wxPG_AUTO_SORT )
4733             Sort();
4734     }
4735 
4736     RecalculateVirtualSize();
4737 }
4738 
4739 // -----------------------------------------------------------------------
4740 // wxPropertyGridInterface property operations
4741 // -----------------------------------------------------------------------
4742 
GetSelection() const4743 wxPGProperty* wxPropertyGridInterface::GetSelection() const
4744 {
4745     return m_pState->GetSelection();
4746 }
4747 
4748 // -----------------------------------------------------------------------
4749 
ClearSelection(bool validation)4750 bool wxPropertyGridInterface::ClearSelection( bool validation )
4751 {
4752     bool res = DoClearSelection(validation, wxPG_SEL_DONT_SEND_EVENT);
4753     wxPropertyGrid* pg = GetPropertyGrid();
4754     if ( pg )
4755         pg->Refresh();
4756     return res;
4757 }
4758 
4759 // -----------------------------------------------------------------------
4760 
DoClearSelection(bool validation,int selFlags)4761 bool wxPropertyGridInterface::DoClearSelection( bool validation,
4762                                                 int selFlags )
4763 {
4764     if ( !validation )
4765         selFlags |= wxPG_SEL_NOVALIDATE;
4766 
4767     wxPropertyGridState* state = m_pState;
4768 
4769     if ( state )
4770     {
4771         wxPropertyGrid* pg = state->GetGrid();
4772         if ( pg->GetState() == state )
4773             return pg->DoSelectProperty(NULL, selFlags);
4774         else
4775             state->DoSetSelection(NULL);
4776     }
4777     return true;
4778 }
4779 
4780 // -----------------------------------------------------------------------
4781 
IsPropertySelected(wxPGPropArg id) const4782 bool wxPropertyGridInterface::IsPropertySelected( wxPGPropArg id ) const
4783 {
4784     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
4785     return m_pState->DoIsPropertySelected(p);
4786 }
4787 
4788 // -----------------------------------------------------------------------
4789 
LimitPropertyEditing(wxPGPropArg id,bool limit)4790 void wxPropertyGridInterface::LimitPropertyEditing( wxPGPropArg id, bool limit )
4791 {
4792     wxPG_PROP_ARG_CALL_PROLOG()
4793 
4794     m_pState->DoLimitPropertyEditing(p, limit);
4795     RefreshProperty(p);
4796 }
4797 
4798 // -----------------------------------------------------------------------
4799 
EnableProperty(wxPGPropArg id,bool enable)4800 bool wxPropertyGridInterface::EnableProperty( wxPGPropArg id, bool enable )
4801 {
4802     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
4803 
4804     wxPropertyGridState* state = p->GetParentState();
4805     wxPropertyGrid* grid = state->GetGrid();
4806 
4807     if ( enable )
4808     {
4809         if ( !(p->m_flags & wxPG_PROP_DISABLED) )
4810             return false;
4811 
4812         // If active, Set active Editor.
4813         if ( grid && grid->GetState() == state && p == grid->GetSelection() )
4814             grid->DoSelectProperty( p, wxPG_SEL_FORCE );
4815     }
4816     else
4817     {
4818         if ( p->m_flags & wxPG_PROP_DISABLED )
4819             return false;
4820 
4821         // If active, Disable as active Editor.
4822         if ( grid && grid->GetState() == state && p == grid->GetSelection() )
4823             grid->DoSelectProperty( p, wxPG_SEL_FORCE );
4824     }
4825 
4826     state->DoEnableProperty(p, enable);
4827 
4828     RefreshProperty( p );
4829 
4830     return true;
4831 }
4832 
4833 // -----------------------------------------------------------------------
4834 
ExpandAll(bool doExpand)4835 bool wxPropertyGridInterface::ExpandAll( bool doExpand )
4836 {
4837     wxPropertyGridState* state = m_pState;
4838 
4839     if ( !state->DoGetRoot()->GetChildCount() )
4840         return true;
4841 
4842     wxPropertyGrid* pg = state->GetGrid();
4843 
4844     if ( GetSelection() && GetSelection() != state->DoGetRoot() &&
4845          !doExpand )
4846     {
4847         pg->ClearSelection(false);
4848     }
4849 
4850     wxPGVIterator it;
4851 
4852     for ( it = GetVIterator( wxPG_ITERATE_ALL ); !it.AtEnd(); it.Next() )
4853     {
4854         wxPGProperty* p = (wxPGProperty*) it.GetProperty();
4855         if ( p->GetChildCount() )
4856         {
4857             if ( doExpand )
4858             {
4859                 if ( !p->IsExpanded() )
4860                 {
4861                     state->DoExpand(p);
4862                 }
4863             }
4864             else
4865             {
4866                 if ( p->IsExpanded() )
4867                 {
4868                     state->DoCollapse(p);
4869                 }
4870             }
4871         }
4872     }
4873 
4874     pg->RecalculateVirtualSize();
4875 
4876     RefreshGrid();
4877 
4878     return true;
4879 }
4880 
4881 // -----------------------------------------------------------------------
4882 // wxPropertyGrid property value setting and getting
4883 // -----------------------------------------------------------------------
4884 
wxPGGetFailed(const wxPGProperty * p,const wxChar * typestr)4885 void wxPGGetFailed( const wxPGProperty* p, const wxChar* typestr )
4886 {
4887     wxPGTypeOperationFailed(p,typestr,wxT("Get"));
4888 }
4889 
4890 // -----------------------------------------------------------------------
4891 
wxPGTypeOperationFailed(const wxPGProperty * p,const wxChar * typestr,const wxChar * op)4892 void wxPGTypeOperationFailed( const wxPGProperty* p, const wxChar* typestr,
4893     const wxChar* op )
4894 {
4895     wxASSERT( p != NULL );
4896     wxLogError( _("Type operation \"%s\" failed: Property labeled \"%s\" is of type \"%s\", NOT \"%s\"."),
4897         op,p->GetLabel().c_str(),p->GetValue().GetType().c_str(),typestr );
4898 }
4899 
4900 // -----------------------------------------------------------------------
4901 
SetPropVal(wxPGPropArg id,wxVariant & value)4902 void wxPropertyGridInterface::SetPropVal( wxPGPropArg id, wxVariant& value )
4903 {
4904     wxPG_PROP_ARG_CALL_PROLOG()
4905 
4906     if ( p )
4907     {
4908         p->SetValue(value);
4909         wxPropertyGrid* propGrid = p->GetGridIfDisplayed();
4910         if ( propGrid )
4911             propGrid->DrawItemAndValueRelated( p );
4912 
4913     }
4914 }
4915 
4916 // -----------------------------------------------------------------------
4917 
SetPropertyValueString(wxPGPropArg id,const wxString & value)4918 void wxPropertyGridInterface::SetPropertyValueString( wxPGPropArg id, const wxString& value )
4919 {
4920     wxPG_PROP_ARG_CALL_PROLOG()
4921 
4922     m_pState->DoSetPropertyValueString(p, value);
4923 }
4924 
4925 // -----------------------------------------------------------------------
4926 // wxPropertyGrid property operations
4927 // -----------------------------------------------------------------------
4928 
DoSetPropertyName(wxPGProperty * p,const wxString & newname)4929 void wxPropertyGrid::DoSetPropertyName( wxPGProperty* p, const wxString& newname )
4930 {
4931     wxCHECK_RET( p, wxT("invalid property id") );
4932 
4933     wxPGProperty* parent = p->GetParent();
4934 
4935     if ( parent->IsCategory() || parent->IsRoot() )
4936     {
4937         if ( p->GetBaseName().length() )
4938             m_pState->m_dictName.erase( wxPGNameConv(p->GetBaseName()) );
4939         if ( newname.length() )
4940             m_pState->m_dictName[newname] = (void*) p;
4941     }
4942 
4943     p->DoSetName(newname);
4944 }
4945 
4946 // -----------------------------------------------------------------------
4947 
EnsureVisible(wxPGPropArg id)4948 bool wxPropertyGrid::EnsureVisible( wxPGPropArg id )
4949 {
4950     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
4951 
4952     Update();
4953 
4954     bool changed = false;
4955 
4956     // Is it inside collapsed section?
4957     if ( !p->IsVisible() )
4958     {
4959         // expand parents
4960         wxPGProperty* parent = p->GetParent();
4961         wxPGProperty* grandparent = parent->GetParent();
4962 
4963         if ( grandparent && grandparent != m_pState->m_properties )
4964             Expand( grandparent );
4965 
4966         Expand( parent );
4967         changed = true;
4968     }
4969 
4970     // Need to scroll?
4971     int vx, vy;
4972     GetViewStart(&vx,&vy);
4973     vy*=wxPG_PIXELS_PER_UNIT;
4974 
4975     int y = p->GetY();
4976 
4977     if ( y < vy )
4978     {
4979         Scroll(vx, y/wxPG_PIXELS_PER_UNIT );
4980         m_iFlags |= wxPG_FL_SCROLLED;
4981         changed = true;
4982     }
4983     else if ( (y+m_lineHeight) > (vy+m_height) )
4984     {
4985         Scroll(vx, (y-m_height+(m_lineHeight*2))/wxPG_PIXELS_PER_UNIT );
4986         m_iFlags |= wxPG_FL_SCROLLED;
4987         changed = true;
4988     }
4989 
4990     if ( changed )
4991         DrawItems( p, p );
4992 
4993     return changed;
4994 }
4995 
4996 // -----------------------------------------------------------------------
4997 // wxPropertyGrid helper methods called by properties
4998 // -----------------------------------------------------------------------
4999 
5000 // Control font changer helper.
SetCurControlBoldFont()5001 void wxPropertyGrid::SetCurControlBoldFont()
5002 {
5003     wxASSERT( m_wndEditor );
5004     m_wndEditor->SetFont( m_captionFont );
5005 }
5006 
5007 // -----------------------------------------------------------------------
5008 
GetGoodEditorDialogPosition(wxPGProperty * p,const wxSize & sz)5009 wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p,
5010                                                      const wxSize& sz )
5011 {
5012 #if wxPG_SMALL_SCREEN
5013     // On small-screen devices, always show dialogs with default position and size.
5014     return wxDefaultPosition;
5015 #else
5016     int splitterX = GetSplitterPosition();
5017     int x = splitterX;
5018     int y = p->GetY();
5019 
5020     wxCHECK_MSG( y >= 0, wxPoint(-1,-1), wxT("invalid y?") );
5021 
5022     ImprovedClientToScreen( &x, &y );
5023 
5024     int sw = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X );
5025     int sh = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y );
5026 
5027     int new_x;
5028     int new_y;
5029 
5030     if ( x > (sw/2) )
5031         // left
5032         new_x = x + (m_width-splitterX) - sz.x;
5033     else
5034         // right
5035         new_x = x;
5036 
5037     if ( y > (sh/2) )
5038         // above
5039         new_y = y - sz.y;
5040     else
5041         // below
5042         new_y = y + m_lineHeight;
5043 
5044     return wxPoint(new_x,new_y);
5045 #endif
5046 }
5047 
5048 // -----------------------------------------------------------------------
5049 
ExpandEscapeSequences(wxString & dst_str,wxString & src_str)5050 wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str )
5051 {
5052     if ( src_str.length() == 0 )
5053     {
5054         dst_str = src_str;
5055         return src_str;
5056     }
5057 
5058     bool prev_is_slash = false;
5059 
5060     wxString::const_iterator i = src_str.begin();
5061 
5062     dst_str.clear();
5063 
5064     for ( ; i != src_str.end(); i++ )
5065     {
5066         wxUniChar a = wxPGGetIterChar(src_str, i);
5067 
5068         if ( a != wxT('\\') )
5069         {
5070             if ( !prev_is_slash )
5071             {
5072                 dst_str << a;
5073             }
5074             else
5075             {
5076                 if ( a == wxT('n') )
5077                 {
5078             #ifdef __WXMSW__
5079                     dst_str << wxT('\n');
5080                     //dst_str << wxT('\10');
5081             #else
5082                     dst_str << wxT('\n');
5083                     //dst_str << 10;
5084             #endif
5085                 }
5086                 else if ( a == wxT('t') )
5087                     dst_str << wxT('\t');
5088                 else
5089                     dst_str << a;
5090             }
5091             prev_is_slash = false;
5092         }
5093         else
5094         {
5095             if ( prev_is_slash )
5096             {
5097                 dst_str << wxT('\\');
5098                 prev_is_slash = false;
5099             }
5100             else
5101             {
5102                 prev_is_slash = true;
5103             }
5104         }
5105     }
5106     return dst_str;
5107 }
5108 
5109 // -----------------------------------------------------------------------
5110 
CreateEscapeSequences(wxString & dst_str,wxString & src_str)5111 wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str )
5112 {
5113     if ( src_str.length() == 0 )
5114     {
5115         dst_str = src_str;
5116         return src_str;
5117     }
5118 
5119     wxString::const_iterator i = src_str.begin();
5120 
5121     dst_str.clear();
5122 
5123     for ( ; i != src_str.end(); i++ )
5124     {
5125         wxChar a = wxPGGetIterChar(src_str, i);
5126 
5127         if ( a >= wxT(' ') )
5128         {
5129             // This surely is not something that requires an escape sequence.
5130             dst_str << a;
5131         }
5132         else
5133         {
5134             // This might need...
5135             if ( a == wxT('\r')  )
5136             {
5137                 // DOS style line end.
5138                 // Already taken care below
5139                 //dst_str = wxT("\\n");
5140                 //src++;
5141             }
5142             else if ( a == wxT('\n') )
5143                 // UNIX style line end.
5144                 dst_str << wxT("\\n");
5145             else if ( a == wxT('\t') )
5146                 // Tab.
5147                 dst_str << wxT('\t');
5148             else
5149             {
5150                 //wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a);
5151                 dst_str << a;
5152             }
5153         }
5154 
5155     }
5156     return dst_str;
5157 }
5158 
5159 // -----------------------------------------------------------------------
5160 // Item iteration macros
5161 //   NB: Nowadays nly needed for alphabetic/categoric mode switching.
5162 // -----------------------------------------------------------------------
5163 
5164 #define II_INVALID_I    0x00FFFFFF
5165 
5166 #define ITEM_ITERATION_VARIABLES \
5167     wxPGProperty* parent; \
5168     unsigned int i; \
5169     unsigned int iMax;
5170 
5171 #define ITEM_ITERATION_INIT_FROM_THE_TOP \
5172     parent = m_properties; \
5173     i = 0;
5174 
5175 #define ITEM_ITERATION_INIT(startparent, startindex, state) \
5176     parent = startparent; \
5177     i = (unsigned int)startindex; \
5178     if ( parent == (wxPGProperty*) NULL ) \
5179     { \
5180         parent = state->m_properties; \
5181         i = 0; \
5182     }
5183 
5184 #define ITEM_ITERATION_LOOP_BEGIN \
5185     do \
5186     { \
5187         iMax = parent->GetCount(); \
5188         while ( i < iMax ) \
5189         {  \
5190             wxPGProperty* p = parent->Item(i);
5191 
5192 #define ITEM_ITERATION_LOOP_END \
5193             if ( p->GetChildCount() ) \
5194             { \
5195                 i = 0; \
5196                 parent = (wxPGProperty*)p; \
5197                 iMax = parent->GetCount(); \
5198             } \
5199             else \
5200                 i++; \
5201         } \
5202         i = parent->m_arrIndex + 1; \
5203         parent = parent->m_parent; \
5204     } \
5205     while ( parent != NULL );
5206 
5207 
5208 // -----------------------------------------------------------------------
5209 
DoGetItemAtY(int y) const5210 wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y ) const
5211 {
5212     // Outside?
5213     if ( y < 0 )
5214         return (wxPGProperty*) NULL;
5215 
5216     unsigned int a = 0;
5217     return m_pState->m_properties->GetItemAtY(y, m_lineHeight, &a);
5218 }
5219 
5220 // -----------------------------------------------------------------------
5221 
HitTest(const wxPoint & pt) const5222 wxPropertyGridHitTestResult wxPropertyGrid::HitTest( const wxPoint& pt ) const
5223 {
5224     wxPoint pt2;
5225     GetViewStart(&pt2.x,&pt2.y);
5226     pt2.x *= wxPG_PIXELS_PER_UNIT;
5227     pt2.y *= wxPG_PIXELS_PER_UNIT;
5228     pt2.x += pt.x;
5229     pt2.y += pt.y;
5230 
5231     return m_pState->HitTest(pt2);
5232 }
5233 
5234 // -----------------------------------------------------------------------
5235 // wxPropertyGrid graphics related methods
5236 // -----------------------------------------------------------------------
5237 
OnPaint(wxPaintEvent & WXUNUSED (event))5238 void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
5239 {
5240     wxPaintDC dc(this);
5241 
5242     // Update everything inside the box
5243     wxRect r = GetUpdateRegion().GetBox();
5244 
5245     // This is workaround for inaccurate GetUpdateRegion() bug
5246     if ( r.x > 0 && r.y > 0 )
5247         r.Inflate(1);
5248 
5249     dc.SetPen(m_colEmptySpace);
5250     dc.SetBrush(m_colEmptySpace);
5251     dc.DrawRectangle(r);
5252 }
5253 
DrawExpanderButton(wxDC & dc,const wxRect & rect,wxPGProperty * property) const5254 void wxPropertyGrid::DrawExpanderButton( wxDC& dc, const wxRect& rect,
5255                                          wxPGProperty* property ) const
5256 {
5257     // Prepare rectangle to be used
5258     wxRect r(rect);
5259     r.x += m_gutterWidth; r.y += m_buttonSpacingY;
5260     r.width = m_iconWidth; r.height = m_iconHeight;
5261 
5262 #if (wxPG_USE_RENDERER_NATIVE)
5263     //
5264 #elif wxPG_ICON_WIDTH
5265     // Drawing expand/collapse button manually
5266     dc.SetPen(m_colPropFore);
5267     if ( property->IsCategory() )
5268         dc.SetBrush(*wxTRANSPARENT_BRUSH);
5269     else
5270         dc.SetBrush(m_colPropBack);
5271 
5272     dc.DrawRectangle( r );
5273     int _y = r.y+(m_iconWidth/2);
5274     dc.DrawLine(r.x+2,_y,r.x+m_iconWidth-2,_y);
5275 #else
5276     wxBitmap* bmp;
5277 #endif
5278 
5279     if ( property->IsExpanded() )
5280     {
5281     // wxRenderer functions are non-mutating in nature, so it
5282     // should be safe to cast "const wxPropertyGrid*" to "wxWindow*".
5283     // Hopefully this does not cause problems.
5284     #if (wxPG_USE_RENDERER_NATIVE)
5285         wxRendererNative::Get().DrawTreeItemButton(
5286                 (wxWindow*)this,
5287                 dc,
5288                 r,
5289                 wxCONTROL_EXPANDED
5290             );
5291     #elif wxPG_ICON_WIDTH
5292         //
5293     #else
5294         bmp = m_collbmp;
5295     #endif
5296 
5297     }
5298     else
5299     {
5300     #if (wxPG_USE_RENDERER_NATIVE)
5301         wxRendererNative::Get().DrawTreeItemButton(
5302                 (wxWindow*)this,
5303                 dc,
5304                 r,
5305                 0
5306             );
5307     #elif wxPG_ICON_WIDTH
5308         int _x = r.x+(m_iconWidth/2);
5309         dc.DrawLine(_x,r.y+2,_x,r.y+m_iconWidth-2);
5310     #else
5311         bmp = m_expandbmp;
5312     #endif
5313     }
5314 
5315 #if (wxPG_USE_RENDERER_NATIVE)
5316     //
5317 #elif wxPG_ICON_WIDTH
5318     //
5319 #else
5320     dc.DrawBitmap( *bmp, r.x, r.y, true );
5321 #endif
5322 }
5323 
5324 // -----------------------------------------------------------------------
5325 
5326 //
5327 // This is the one called by OnPaint event handler and others.
5328 // topy and bottomy are already unscrolled (ie. physical)
5329 //
DrawItems(wxDC & dc,unsigned int topy,unsigned int bottomy,const wxRect * clipRect)5330 void wxPropertyGrid::DrawItems( wxDC& dc,
5331                                 unsigned int topy,
5332                                 unsigned int bottomy,
5333                                 const wxRect* clipRect )
5334 {
5335     if ( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) return;
5336 
5337     m_pState->EnsureVirtualHeight();
5338 
5339     wxRect tempClipRect;
5340     if ( !clipRect )
5341     {
5342         tempClipRect = wxRect(0,topy,m_pState->m_width,bottomy);
5343         clipRect = &tempClipRect;
5344     }
5345 
5346     // items added check
5347     if ( m_pState->m_itemsAdded ) PrepareAfterItemsAdded();
5348 
5349     int paintFinishY = 0;
5350 
5351     if ( m_pState->m_properties->GetCount() > 0 )
5352     {
5353         wxDC* dcPtr = &dc;
5354         bool isBuffered = false;
5355 
5356     #if wxPG_DOUBLE_BUFFER
5357         wxMemoryDC* bufferDC = NULL;
5358 
5359         if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
5360         {
5361             if ( !m_doubleBuffer )
5362             {
5363                 paintFinishY = clipRect->y;
5364                 dcPtr = NULL;
5365             }
5366             else
5367             {
5368                 bufferDC = new wxMemoryDC();
5369 
5370                 // If nothing was changed, then just copy from double-buffer
5371                 bufferDC->SelectObject( *m_doubleBuffer );
5372                 dcPtr = bufferDC;
5373 
5374                 isBuffered = true;
5375             }
5376         }
5377     #endif
5378 
5379         if ( dcPtr )
5380         {
5381             paintFinishY = DoDrawItems(*dcPtr, NULL, NULL, clipRect,
5382                                        isBuffered);
5383 
5384             if ( paintFinishY <= (clipRect->y+clipRect->height) )
5385             {
5386                 dcPtr->SetPen(m_colEmptySpace);
5387                 dcPtr->SetBrush(m_colEmptySpace);
5388                 dcPtr->DrawRectangle(0, paintFinishY, m_width,
5389                                      dcPtr->GetSize().y - paintFinishY);
5390             }
5391         }
5392 
5393     #if wxPG_DOUBLE_BUFFER
5394         if ( bufferDC )
5395         {
5396             dc.Blit(clipRect->x, clipRect->y, clipRect->width,
5397                     clipRect->height,
5398                     bufferDC, 0, 0, wxCOPY );
5399             delete bufferDC;
5400         }
5401     #endif
5402     }
5403     else
5404     {
5405         dc.SetPen(m_colEmptySpace);
5406         dc.SetBrush(m_colEmptySpace);
5407         dc.DrawRectangle(*clipRect);
5408     }
5409 }
5410 
5411 // -----------------------------------------------------------------------
5412 
DoDrawItems(wxDC & dc,const wxPGProperty * firstItem,const wxPGProperty * lastItem,const wxRect * clipRect,bool isBuffered) const5413 int wxPropertyGrid::DoDrawItems( wxDC& dc,
5414                                  const wxPGProperty* firstItem,
5415                                  const wxPGProperty* lastItem,
5416                                  const wxRect* clipRect,
5417                                  bool isBuffered ) const
5418 {
5419     // TODO: This should somehow be eliminated.
5420     wxRect tempClipRect;
5421     if ( !clipRect )
5422     {
5423         wxASSERT(firstItem);
5424         wxASSERT(lastItem);
5425         tempClipRect = GetPropertyRect(firstItem, lastItem);
5426         clipRect = &tempClipRect;
5427     }
5428 
5429     if ( !firstItem )
5430         firstItem = DoGetItemAtY(clipRect->y);
5431 
5432     if ( !lastItem )
5433     {
5434         lastItem = DoGetItemAtY(clipRect->y+clipRect->height-1);
5435         if ( !lastItem )
5436             lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
5437     }
5438 
5439     if ( m_frozen || m_height < 1 || firstItem == NULL )
5440         return 0;
5441 
5442     wxCHECK_MSG( !m_pState->m_itemsAdded, clipRect->y, wxT("no items added") );
5443     wxASSERT( m_pState->m_properties->GetCount() );
5444 
5445     int lh = m_lineHeight;
5446 
5447     int firstItemTopY;
5448     int lastItemBottomY;
5449 
5450     firstItemTopY = clipRect->y;
5451     lastItemBottomY = clipRect->y + clipRect->height;
5452 
5453     // Align y coordinates to item boundaries
5454     firstItemTopY -= firstItemTopY % lh;
5455     lastItemBottomY += lh - (lastItemBottomY % lh);
5456     lastItemBottomY -= 1;
5457 
5458     // Entire range outside scrolled, visible area?
5459     if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || lastItemBottomY <= 0 )
5460         return 0;
5461 
5462     wxCHECK_MSG( firstItemTopY < lastItemBottomY, clipRect->y,
5463                  wxT("invalid y values") );
5464 
5465     /*
5466     wxLogDebug(wxT("  -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"),
5467         firstItem->GetLabel().c_str(),
5468         lastItem->GetLabel().c_str(),
5469         (int)(lastItemBottomY - firstItemTopY),
5470         (int)m_height,
5471         (unsigned long)clipRect );
5472     */
5473 
5474     wxRect r;
5475 
5476     long windowStyle = m_windowStyle;
5477 
5478     int xRelMod = 0;
5479     int yRelMod = 0;
5480 
5481     //
5482     // With wxPG_DOUBLE_BUFFER, do double buffering
5483     // - buffer's y = 0, so align cliprect and coordinates to that
5484     //
5485 #if wxPG_DOUBLE_BUFFER
5486 
5487     wxRect cr2;
5488 
5489     if ( isBuffered )
5490     {
5491         xRelMod = clipRect->x;
5492         yRelMod = clipRect->y;
5493 
5494         //
5495         // clipRect conversion
5496         if ( clipRect )
5497         {
5498             cr2 = *clipRect;
5499             cr2.x -= xRelMod;
5500             cr2.y -= yRelMod;
5501             clipRect = &cr2;
5502         }
5503         firstItemTopY -= yRelMod;
5504         lastItemBottomY -= yRelMod;
5505     }
5506 #else
5507     wxUnusedVar(isBuffered);
5508 #endif
5509 
5510     int x = m_marginWidth - xRelMod;
5511 
5512     wxFont normalfont = GetFont();
5513 
5514     bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) ? true : false;
5515 
5516     bool isEnabled = IsEnabled();
5517 
5518     wxPGProperty* firstSelected = GetSelection();
5519 
5520     //
5521     // Prepare some pens and brushes that are often changed to.
5522     //
5523 
5524     wxBrush marginBrush(m_colMargin);
5525     wxPen marginPen(m_colMargin);
5526     wxBrush capbgbrush(m_colCapBack,wxSOLID);
5527     wxPen linepen(m_colLine,1,wxSOLID);
5528 
5529     wxBrush selBackBrush;
5530     if ( isEnabled )
5531         selBackBrush = wxBrush(m_colSelBack);
5532     else
5533         selBackBrush = marginBrush;
5534 
5535     // pen that has same colour as text
5536     wxPen outlinepen(m_colPropFore,1,wxSOLID);
5537 
5538     //
5539     // Clear margin with background colour
5540     //
5541     dc.SetBrush( marginBrush );
5542     if ( !(windowStyle & wxPG_HIDE_MARGIN) )
5543     {
5544         dc.SetPen( *wxTRANSPARENT_PEN );
5545         dc.DrawRectangle(-1-xRelMod,firstItemTopY-1,x+2,lastItemBottomY-firstItemTopY+2);
5546     }
5547 
5548     const wxPropertyGridState* state = m_pState;
5549 
5550 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
5551     bool wasSelectedPainted = false;
5552 #endif
5553 
5554     // TODO: Only render columns that are within clipping region.
5555 
5556     dc.SetFont(normalfont);
5557 
5558     wxPropertyGridConstIterator it( state, wxPG_ITERATE_VISIBLE, firstItem );
5559     int endScanBottomY = lastItemBottomY + lh;
5560     int y = firstItemTopY;
5561     unsigned int arrInd = 0;
5562 
5563     for ( ; !it.AtEnd(); it.Next() )
5564     {
5565         const wxPGProperty* p = *it;
5566         //wxLogDebug(wxT("%s, %i < %i"),p.GetName().c_str(), y, endScanBottomY);
5567 
5568         if ( !p->HasFlag(wxPG_PROP_HIDDEN) )
5569         {
5570             m_visPropArray[arrInd] = (wxPGProperty*)p;
5571             arrInd++;
5572 
5573             if ( y > endScanBottomY )
5574                 break;
5575 
5576             y += lh;
5577         }
5578     }
5579 
5580     m_visPropArray[arrInd] = NULL;
5581 
5582     int gridWidth = state->m_width;
5583     int rowHeight = m_fontHeight+(m_spacingy*2)+1;
5584 
5585     y = firstItemTopY;
5586     for ( arrInd=0;
5587           m_visPropArray[arrInd] != NULL && y <= lastItemBottomY;
5588           arrInd++ )
5589     {
5590         wxPGProperty* p =(wxPGProperty*) m_visPropArray[arrInd];
5591         wxPGProperty* nextP = (wxPGProperty*) m_visPropArray[arrInd+1];
5592         wxPGProperty* parent = p->GetParent();
5593 
5594         int textMarginHere = x;
5595         int renderFlags = 0;
5596 
5597         int greyDepth = m_marginWidth;
5598         if ( !(windowStyle & wxPG_HIDE_CATEGORIES) )
5599             greyDepth = (((int)p->m_depthBgCol)-1) * m_subgroup_extramargin + m_marginWidth;
5600 
5601         int greyDepthX = greyDepth - xRelMod;
5602 
5603         // Use basic depth if in non-categoric mode and parent is base array.
5604         if ( !(windowStyle & wxPG_HIDE_CATEGORIES) || parent != m_pState->m_properties )
5605         {
5606             textMarginHere += ((unsigned int)((p->m_depth-1)*m_subgroup_extramargin));
5607         }
5608 
5609         // Paint margin area
5610         dc.SetBrush(marginBrush);
5611         dc.SetPen(marginPen);
5612         dc.DrawRectangle( -xRelMod, y, greyDepth, lh );
5613 
5614         dc.SetPen( linepen );
5615 
5616         int y2 = y + lh;
5617 
5618 #ifdef __WXMSW__
5619         // Margin Edge
5620         // Let's not draw a margin if wxPG_HIDE_MARGIN is specified, since it
5621         // looks better, at least under Windows when we have a themed border
5622         // (the themed-window-specific whitespace between the real border and
5623         // the propgrid margin exacerbates the double-border look).
5624 
5625         // Is this or its parent themed?
5626         bool suppressMarginEdge = (GetWindowStyle() & wxPG_HIDE_MARGIN) &&
5627             (((GetWindowStyle() & wxBORDER_MASK) == wxBORDER_THEME) ||
5628             (((GetWindowStyle() & wxBORDER_MASK) == wxBORDER_NONE) &&
5629         ((GetParent()->GetWindowStyle() & wxBORDER_MASK) == wxBORDER_THEME)));
5630 #else
5631         bool suppressMarginEdge = false;
5632 #endif
5633 
5634         if ( !suppressMarginEdge )
5635         {
5636             dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
5637         }
5638         else
5639         {
5640             // Blank out the margin edge
5641             dc.SetPen(wxPen(GetBackgroundColour()));
5642         	dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
5643             dc.SetPen( linepen );
5644         }
5645 
5646         // Splitters
5647         unsigned int si;
5648         int sx = x;
5649 
5650         for ( si=0; si<state->m_colWidths.size(); si++ )
5651         {
5652             sx += state->m_colWidths[si];
5653             dc.DrawLine( sx, y, sx, y2 );
5654         }
5655 
5656         // Horizontal Line, below
5657         //   (not if both this and next is category caption)
5658         if ( p->IsCategory() &&
5659              nextP && nextP->IsCategory() )
5660             dc.SetPen(m_colCapBack);
5661 
5662         dc.DrawLine( greyDepthX, y2-1, gridWidth-xRelMod, y2-1 );
5663 
5664         bool isSelected = state->DoIsPropertySelected(p);
5665 
5666         if ( p == firstSelected )
5667         {
5668             renderFlags |= wxPGCellRenderer::Selected;
5669 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
5670             wasSelectedPainted = true;
5671 #endif
5672         }
5673 
5674         wxColour rowBgCol;
5675         wxColour rowFgCol;
5676         wxBrush rowBgBrush;
5677 
5678         if ( p->IsCategory() )
5679         {
5680             if ( p->m_fgColIndex == 0 )
5681                 rowFgCol = m_colCapFore;
5682             else
5683                 rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex];
5684             rowBgBrush = wxBrush(m_colCapBack);
5685         }
5686         else if ( !isSelected )
5687         {
5688             // Disabled may get different colour.
5689             if ( !p->IsEnabled() )
5690                 rowFgCol = m_colDisPropFore;
5691             else
5692                 rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex];
5693 
5694             rowBgBrush = *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex];
5695         }
5696         else
5697         {
5698             // Selected gets different colour.
5699             if ( reallyFocused && p == firstSelected )
5700             {
5701                 rowFgCol = m_colSelFore;
5702                 rowBgBrush = selBackBrush;
5703             }
5704             else if ( isEnabled )
5705             {
5706                 rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex];
5707                 if ( p == firstSelected )
5708                     rowBgBrush = marginBrush;
5709                 else
5710                     rowBgBrush = selBackBrush;
5711             }
5712             else
5713             {
5714                 rowFgCol = m_colDisPropFore;
5715                 rowBgBrush = selBackBrush;
5716             }
5717         }
5718 
5719         bool fontChanged = false;
5720 
5721         wxRect butRect( ((p->m_depth - 1) * m_subgroup_extramargin) - xRelMod,
5722                         y,
5723                         m_marginWidth,
5724                         lh );
5725 
5726         if ( p->IsCategory() )
5727         {
5728             // Captions are all cells merged as one
5729             dc.SetFont(m_captionFont);
5730             fontChanged = true;
5731             wxRect cellRect(greyDepthX, y, gridWidth - greyDepth + 2, rowHeight-1 );
5732 
5733             dc.SetBrush(rowBgBrush);
5734             dc.SetPen(rowBgBrush.GetColour());
5735             dc.SetTextForeground(rowFgCol);
5736 
5737             dc.DrawRectangle(cellRect);
5738 
5739             // Foreground
5740             wxPGCellRenderer* renderer = p->GetCellRenderer(0);
5741             renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags );
5742 
5743             // Tree Item Button
5744             if ( !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
5745                 DrawExpanderButton( dc, butRect, p );
5746         }
5747         else
5748         {
5749             // Fine tune button rectangle to actually fit the cell
5750             if ( butRect.x > 0 )
5751                 butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST;
5752 
5753             if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) )
5754             {
5755                 dc.SetFont(m_captionFont);
5756                 fontChanged = true;
5757             }
5758 
5759             unsigned int ci;
5760             int cellX = x + 1;
5761             int nextCellWidth = state->m_colWidths[0] -
5762                                 (greyDepthX - m_marginWidth);
5763             wxRect cellRect(greyDepthX+1, y, 0, rowHeight-1);
5764             int textXAdd = textMarginHere - greyDepthX;
5765 
5766             for ( ci=0; ci<state->m_colWidths.size(); ci++ )
5767             {
5768                 cellRect.width = nextCellWidth - 1;
5769 
5770                 wxWindow* cellEditor = NULL;
5771                 wxWindow* editorClipperWnd = NULL;
5772 
5773                 // Tree Item Button (must be drawn before clipping is set up
5774                 // if it is outside the cell proper)
5775                 bool drawExpanderButton = ( ci == 0 &&
5776                                             p->HasVisibleChildren() &&
5777                                             !HasFlag(wxPG_HIDE_MARGIN) );
5778                 bool drawExpanderButtonPostClip = false;
5779 
5780                 if ( drawExpanderButton )
5781                 {
5782                     drawExpanderButtonPostClip = true;
5783                     // Coordinate-wise, the expander button rectangle
5784                     // can be partially left of the cell rectangle.
5785                     if ( butRect.x < cellRect.x )
5786                         DrawExpanderButton(dc, butRect, p);
5787                     else
5788                         drawExpanderButtonPostClip = true;
5789                 }
5790 
5791                 // Background
5792                 if ( isSelected && (ci == 1 || ci == m_selColumn) )
5793                 {
5794                     if ( p == firstSelected )
5795                     {
5796                         if ( ci == 1 && m_wndEditor )
5797                         {
5798                             editorClipperWnd = m_wndEditor;
5799                             cellEditor = GetEditorControl();
5800                         }
5801                         else if ( ci == m_selColumn && m_labelEditor )
5802                         {
5803                             editorClipperWnd = m_labelEditor;
5804                             cellEditor = GetLabelEditor();
5805                         }
5806                     }
5807 
5808                     if ( cellEditor )
5809                     {
5810                         wxColour editorBgCol = cellEditor->GetBackgroundColour();
5811                         dc.SetBrush(editorBgCol);
5812                         dc.SetPen(editorBgCol);
5813                         dc.SetTextForeground(m_colPropFore);
5814 
5815                         if ( m_dragStatus != 0 || (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) )
5816                             cellEditor = NULL;
5817                     }
5818                     else
5819                     {
5820                         dc.SetBrush(m_colPropBack);
5821                         dc.SetPen(m_colPropBack);
5822                         if ( p->IsEnabled() )
5823                             dc.SetTextForeground(m_colPropFore);
5824                         else
5825                             dc.SetTextForeground(m_colDisPropFore);
5826                     }
5827                 }
5828                 else
5829                 {
5830                     dc.SetBrush(rowBgBrush);
5831                     dc.SetPen(rowBgBrush.GetColour());
5832                     dc.SetTextForeground(rowFgCol);
5833                 }
5834 
5835                 if ( cellEditor )
5836                 {
5837                     wxRegion clipRegion(cellRect);
5838                     clipRegion.Subtract(editorClipperWnd->GetRect());
5839                     dc.SetClippingRegion(clipRegion);
5840                 }
5841                 else
5842                 {
5843                     dc.SetClippingRegion(cellRect);
5844                 }
5845 
5846                 dc.DrawRectangle(cellRect);
5847 
5848                 if ( drawExpanderButtonPostClip )
5849                     DrawExpanderButton(dc, butRect, p);
5850 
5851                 cellRect.x += textXAdd;
5852                 cellRect.width -= textXAdd;
5853 
5854                 // Foreground
5855                 if ( !cellEditor )
5856                 {
5857                     wxPGCellRenderer* renderer;
5858                     int cmnVal = p->GetCommonValue();
5859                     if ( cmnVal == -1 || ci != 1 )
5860                     {
5861                         renderer = p->GetCellRenderer(ci);
5862                         renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags );
5863                     }
5864                     else
5865                     {
5866                         renderer = GetCommonValue(cmnVal)->GetRenderer();
5867                         renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags );
5868                     }
5869                 }
5870 
5871                 cellX += state->m_colWidths[ci];
5872                 if ( ci < (state->m_colWidths.size()-1) )
5873                     nextCellWidth = state->m_colWidths[ci+1];
5874                 cellRect.x = cellX;
5875                 dc.DestroyClippingRegion(); // Is this really necessary?
5876                 textXAdd = 0;
5877             }
5878         }
5879 
5880         if ( fontChanged )
5881             dc.SetFont(normalfont);
5882 
5883         y += rowHeight;
5884     }
5885 
5886     // Refresh editor controls (seems not needed on msw)
5887     // NOTE: This code is mandatory for GTK!
5888 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
5889     if ( wasSelectedPainted )
5890     {
5891         if ( m_wndEditor )
5892             m_wndEditor->Refresh();
5893         if ( m_wndEditor2 )
5894             m_wndEditor2->Refresh();
5895     }
5896 #endif
5897 
5898     return y;
5899 }
5900 
5901 // -----------------------------------------------------------------------
5902 
GetPropertyRect(const wxPGProperty * p1,const wxPGProperty * p2) const5903 wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProperty* p2 ) const
5904 {
5905     wxRect r;
5906 
5907     if ( m_width < 10 || m_height < 10 ||
5908          !m_pState->m_properties->GetCount() ||
5909          p1 == (wxPGProperty*) NULL )
5910         return wxRect(0,0,0,0);
5911 
5912     int vy = 0;
5913 
5914     //
5915     // Return rect which encloses the given property range
5916 
5917     int visTop = p1->GetY();
5918     int visBottom;
5919     if ( p2 )
5920         visBottom = p2->GetY() + m_lineHeight;
5921     else
5922         visBottom = m_height + visTop;
5923 
5924     // If seleced property is inside the range, we'll extend the range to include
5925     // control's size.
5926     wxPGProperty* selected = GetSelection();
5927     if ( selected )
5928     {
5929         int selectedY = selected->GetY();
5930         if ( selectedY >= visTop && selectedY < visBottom )
5931         {
5932             wxWindow* editor = GetEditorControl();
5933             if ( editor )
5934             {
5935                 int visBottom2 = selectedY + editor->GetSize().y;
5936                 if ( visBottom2 > visBottom )
5937                     visBottom = visBottom2;
5938             }
5939         }
5940     }
5941 
5942     return wxRect(0,visTop-vy,m_pState->m_width,visBottom-visTop);
5943 }
5944 
5945 // -----------------------------------------------------------------------
5946 
DrawItems(const wxPGProperty * p1,const wxPGProperty * p2)5947 void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 )
5948 {
5949     if ( m_frozen )
5950         return;
5951 
5952     if ( m_pState->m_itemsAdded )
5953         PrepareAfterItemsAdded();
5954 
5955     wxRect rect = GetPropertyRect(p1, p2);
5956     if ( rect.width <= 0 )
5957         return;
5958 
5959     //
5960     // Reduce editor rects
5961     wxRegion region(rect);
5962     if ( m_wndEditor )
5963         region.Subtract(m_wndEditor->GetRect());
5964     if ( m_wndEditor2 )
5965         region.Subtract(m_wndEditor2->GetRect());
5966     if ( m_labelEditor )
5967         region.Subtract(m_labelEditor->GetRect());
5968 
5969     wxRegionIterator rects(region);
5970 
5971     while ( rects )
5972     {
5973         m_canvas->RefreshRect(rects.GetRect(), false);
5974         rects++;
5975     }
5976 }
5977 
5978 // -----------------------------------------------------------------------
5979 
RefreshProperty(wxPGProperty * p)5980 void wxPropertyGrid::RefreshProperty( wxPGProperty* p )
5981 {
5982     if ( m_pState->DoIsPropertySelected(p) || p->IsChildSelected(true) )
5983     {
5984         // NB: We must copy the selection.
5985         wxArrayPGProperty selection = m_pState->m_selection;
5986         DoSetSelection(selection, wxPG_SEL_FORCE);
5987     }
5988 
5989     DrawItemAndChildren(p);
5990 }
5991 
5992 // -----------------------------------------------------------------------
5993 
RefreshEditor()5994 void wxPropertyGrid::RefreshEditor()
5995 {
5996     wxPGProperty* p = GetSelection();
5997     if ( !p )
5998         return;
5999 
6000     wxWindow* wnd = GetEditorControl();
6001     if ( !wnd )
6002         return;
6003 
6004     // Set editor font boldness - must do this before
6005     // calling UpdateControl().
6006     if ( HasFlag(wxPG_BOLD_MODIFIED) )
6007     {
6008         if ( p->HasFlag(wxPG_PROP_MODIFIED) )
6009             wnd->SetFont(GetCaptionFont());
6010         else
6011             wnd->SetFont(GetFont());
6012     }
6013 
6014     const wxPGEditor* editorClass = p->GetEditorClass();
6015 
6016     editorClass->UpdateControl(p, wnd);
6017 
6018     if ( p->IsValueUnspecified() )
6019     {
6020         editorClass->SetValueToUnspecified(p, wnd);
6021         SetEditorAppearance(m_unspecifiedAppearance);
6022     }
6023 }
6024 
6025 // -----------------------------------------------------------------------
6026 
DrawItemAndValueRelated(wxPGProperty * p)6027 void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty* p )
6028 {
6029     if ( m_frozen )
6030         return;
6031 
6032     // Draw item, children, and parent too, if it is not category
6033     wxPGProperty* parent = p->GetParent();
6034 
6035     while ( parent &&
6036             !parent->IsCategory() &&
6037             parent->GetParent() )
6038     {
6039          DrawItem(parent);
6040          parent = parent->GetParent();
6041     }
6042 
6043     DrawItemAndChildren(p);
6044 }
6045 
DrawItemAndChildren(wxPGProperty * p)6046 void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p )
6047 {
6048     wxCHECK_RET( p, wxT("invalid property id") );
6049 
6050     // Do not draw if in non-visible page
6051     if ( p->GetParentState() != m_pState )
6052         return;
6053 
6054     // do not draw a single item if multiple pending
6055     if ( m_pState->m_itemsAdded || m_frozen )
6056         return;
6057 
6058     // Update child control.
6059     wxPGProperty* selected = GetSelection();
6060     if ( selected && selected->GetParent() == p )
6061         RefreshEditor();
6062 
6063     const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem();
6064 
6065     DrawItems(p, lastDrawn);
6066 }
6067 
6068 // -----------------------------------------------------------------------
6069 
Refresh(bool WXUNUSED (eraseBackground),const wxRect * rect)6070 void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground),
6071                               const wxRect *rect )
6072 {
6073     PrepareAfterItemsAdded();
6074 
6075     wxWindow::Refresh(false);
6076     if ( m_canvas )
6077         // TODO: Coordinate translation
6078         m_canvas->Refresh(false, rect);
6079 
6080 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
6081     // I think this really helps only GTK+1.2
6082     if ( m_wndEditor ) m_wndEditor->Refresh();
6083     if ( m_wndEditor2 ) m_wndEditor2->Refresh();
6084 #endif
6085 }
6086 
6087 // -----------------------------------------------------------------------
6088 // wxPropertyGrid global operations
6089 // -----------------------------------------------------------------------
6090 
Clear()6091 void wxPropertyGrid::Clear()
6092 {
6093     m_pState->DoClear();
6094 
6095     m_propHover = NULL;
6096 
6097     m_prevVY = 0;
6098 
6099     RecalculateVirtualSize();
6100 
6101     // Need to clear some area at the end
6102     if ( !m_frozen )
6103         RefreshRect(wxRect(0, 0, m_width, m_height));
6104 }
6105 
6106 // -----------------------------------------------------------------------
6107 
EnableCategories(bool enable)6108 bool wxPropertyGrid::EnableCategories( bool enable )
6109 {
6110     ClearSelection(false);
6111 
6112     if ( enable )
6113     {
6114         //
6115         // Enable categories
6116         //
6117 
6118         m_windowStyle &= ~(wxPG_HIDE_CATEGORIES);
6119     }
6120     else
6121     {
6122         //
6123         // Disable categories
6124         //
6125         m_windowStyle |= wxPG_HIDE_CATEGORIES;
6126     }
6127 
6128     if ( !m_pState->EnableCategories(enable) )
6129         return false;
6130 
6131     if ( !m_frozen )
6132     {
6133         if ( m_windowStyle & wxPG_AUTO_SORT )
6134         {
6135             m_pState->m_itemsAdded = 1; // force
6136             PrepareAfterItemsAdded();
6137         }
6138     }
6139     else
6140         m_pState->m_itemsAdded = 1;
6141 
6142     // No need for RecalculateVirtualSize() here - it is already called in
6143     // wxPropertyGridState method above.
6144 
6145     Refresh();
6146 
6147     return true;
6148 }
6149 
6150 // -----------------------------------------------------------------------
6151 
SwitchState(wxPropertyGridState * pNewState)6152 void wxPropertyGrid::SwitchState( wxPropertyGridState* pNewState )
6153 {
6154     wxASSERT( pNewState );
6155     wxASSERT( pNewState->GetGrid() );
6156 
6157     if ( pNewState == m_pState )
6158         return;
6159 
6160     wxArrayPGProperty oldSelection = m_pState->m_selection;
6161 
6162     // Call ClearSelection() instead of DoClearSelection()
6163     // so that selection clear events are not sent.
6164     ClearSelection();
6165 
6166     m_pState->m_selection = oldSelection;
6167 
6168     bool orig_mode = m_pState->IsInNonCatMode();
6169     bool new_state_mode = pNewState->IsInNonCatMode();
6170 
6171     m_pState = pNewState;
6172 
6173     // Validate width
6174     int pgWidth = GetClientSize().x;
6175     if ( HasVirtualWidth() )
6176     {
6177         int minWidth = pgWidth;
6178         if ( pNewState->m_width < minWidth )
6179         {
6180             pNewState->m_width = minWidth;
6181             pNewState->CheckColumnWidths();
6182         }
6183     }
6184     else
6185     {
6186         //
6187         // Just in case, fully re-center splitter
6188         if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) )
6189             pNewState->m_fSplitterX = -1.0;
6190 
6191         pNewState->OnClientWidthChange( pgWidth, pgWidth - pNewState->m_width );
6192     }
6193 
6194     m_propHover = (wxPGProperty*) NULL;
6195 
6196     // If necessary, convert state to correct mode.
6197     if ( orig_mode != new_state_mode )
6198     {
6199         // This should refresh as well.
6200         EnableCategories( orig_mode?false:true );
6201     }
6202     else if ( !m_frozen )
6203     {
6204         // Refresh, if not frozen.
6205         if ( m_pState->m_itemsAdded )
6206             PrepareAfterItemsAdded();
6207 
6208         // Reselect (Use SetSelection() instead of Do-variant so that
6209         // events won't be sent).
6210         SetSelection(m_pState->m_selection);
6211 
6212         RecalculateVirtualSize(0);
6213         Refresh();
6214     }
6215     else
6216         m_pState->m_itemsAdded = 1;
6217 }
6218 
6219 // -----------------------------------------------------------------------
6220 
Sort(wxPGPropArg id)6221 void wxPropertyGrid::Sort( wxPGPropArg id )
6222 {
6223     wxPG_PROP_ARG_CALL_PROLOG()
6224 
6225     m_pState->Sort( p );
6226 }
6227 
6228 // -----------------------------------------------------------------------
6229 
Sort()6230 void wxPropertyGrid::Sort()
6231 {
6232     m_pState->Sort();
6233 
6234     // Y-position of any open property editor may have changed
6235     CorrectEditorWidgetPosY();
6236 }
6237 
6238 // -----------------------------------------------------------------------
6239 
6240 // Call to SetSplitterPosition will always disable splitter auto-centering
6241 // if parent window is shown.
DoSetSplitterPosition_(int newxpos,bool refresh,int splitterIndex,bool allPages)6242 void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int splitterIndex, bool allPages )
6243 {
6244     if ( ( newxpos < wxPG_DRAG_MARGIN ) )
6245         return;
6246 
6247     wxPropertyGridState* state = m_pState;
6248 
6249     state->DoSetSplitterPosition( newxpos, splitterIndex, allPages );
6250 
6251     if ( refresh )
6252     {
6253         if ( GetSelection() )
6254             CorrectEditorWidgetSizeX();
6255 
6256         Refresh();
6257     }
6258 }
6259 
6260 // -----------------------------------------------------------------------
6261 
ResetColumnSizes(bool enableAutoResizing)6262 void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing )
6263 {
6264     wxPropertyGridState* state = m_pState;
6265     if ( state )
6266         state->ResetColumnSizes(false);
6267 
6268     if ( enableAutoResizing && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
6269         m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
6270 }
6271 
6272 // -----------------------------------------------------------------------
6273 
CenterSplitter(bool enableAutoResizing)6274 void wxPropertyGrid::CenterSplitter( bool enableAutoResizing )
6275 {
6276     SetSplitterPosition( m_width/2, true );
6277     if ( enableAutoResizing && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
6278         m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
6279 }
6280 
6281 // -----------------------------------------------------------------------
6282 // wxPropertyGrid item iteration (GetNextProperty etc.) methods
6283 // -----------------------------------------------------------------------
6284 
6285 // Returns nearest paint visible property (such that will be painted unless
6286 // window is scrolled or resized). If given property is paint visible, then
6287 // it itself will be returned
GetNearestPaintVisible(wxPGProperty * p) const6288 wxPGProperty* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty* p ) const
6289 {
6290     int vx,vy1;// Top left corner of client
6291     GetViewStart(&vx,&vy1);
6292     vy1 *= wxPG_PIXELS_PER_UNIT;
6293 
6294     int vy2 = vy1 + m_height;
6295     int propY = p->GetY2(m_lineHeight);
6296 
6297     if ( (propY + m_lineHeight) < vy1 )
6298     {
6299     // Too high
6300         return DoGetItemAtY( vy1 );
6301     }
6302     else if ( propY > vy2 )
6303     {
6304     // Too low
6305         return DoGetItemAtY( vy2 );
6306     }
6307 
6308     // Itself paint visible
6309     return p;
6310 
6311 }
6312 
6313 // -----------------------------------------------------------------------
6314 
SetButtonShortcut(int keycode,bool ctrlDown,bool altDown)6315 void wxPropertyGrid::SetButtonShortcut( int keycode, bool ctrlDown, bool altDown )
6316 {
6317     if ( keycode )
6318     {
6319         m_pushButKeyCode = keycode;
6320         m_pushButKeyCodeNeedsCtrl = ctrlDown ? 1 : 0;
6321         m_pushButKeyCodeNeedsAlt = altDown ? 1 : 0;
6322     }
6323     else
6324     {
6325         m_pushButKeyCode = WXK_DOWN;
6326         m_pushButKeyCodeNeedsCtrl = 0;
6327         m_pushButKeyCodeNeedsAlt = 1;
6328     }
6329 }
6330 
6331 // -----------------------------------------------------------------------
6332 // Methods related to change in value, value modification and sending events
6333 // -----------------------------------------------------------------------
6334 
6335 // commits any changes in editor of selected property
6336 // return true if validation did not fail
6337 // flags are same as with DoSelectProperty
CommitChangesFromEditor(wxUint32 flags)6338 bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags )
6339 {
6340     // Committing already?
6341     if ( m_inCommitChangesFromEditor )
6342         return true;
6343 
6344     // Don't do this if already processing editor event. It might
6345     // induce recursive dialogs and crap like that.
6346     if ( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT )
6347     {
6348         if ( m_inDoPropertyChanged )
6349             return true;
6350 
6351         return false;
6352     }
6353 
6354     wxPGProperty* selected = GetSelection();
6355 
6356     if ( m_wndEditor &&
6357          IsEditorsValueModified() &&
6358          (m_iFlags & wxPG_FL_INITIALIZED) &&
6359          selected )
6360     {
6361         m_inCommitChangesFromEditor = 1;
6362 
6363         wxVariant variant(selected->GetValueRef());
6364         bool valueIsPending = false;
6365 
6366         // JACS - necessary to avoid new focus being found spuriously within OnIdle
6367         // due to another window getting focus
6368         wxWindow* oldFocus = m_curFocused;
6369 
6370         bool validationFailure = false;
6371         bool forceSuccess = (flags & (wxPG_SEL_NOVALIDATE|wxPG_SEL_FORCE)) ? true : false;
6372 
6373         m_chgInfo_changedProperty = NULL;
6374 
6375         // If truly modified, schedule value as pending.
6376         if ( selected->GetEditorClass()->ActualGetValueFromControl( variant, selected, GetEditorControl() ) )
6377         {
6378             if ( DoEditorValidate() &&
6379                  PerformValidation(selected, variant) )
6380             {
6381                 valueIsPending = true;
6382             }
6383             else
6384             {
6385                 validationFailure = true;
6386             }
6387         }
6388         else
6389         {
6390             EditorsValueWasNotModified();
6391         }
6392 
6393         bool res = true;
6394 
6395         m_inCommitChangesFromEditor = 0;
6396 
6397         if ( validationFailure && !forceSuccess )
6398         {
6399             if (oldFocus)
6400             {
6401                 oldFocus->SetFocus();
6402                 m_curFocused = oldFocus;
6403             }
6404 
6405             res = OnValidationFailure(selected, variant);
6406 
6407             // Now prevent further validation failure messages
6408             if ( res )
6409             {
6410                 EditorsValueWasNotModified();
6411                 OnValidationFailureReset(selected);
6412             }
6413         }
6414         else if ( valueIsPending )
6415         {
6416             DoPropertyChanged( selected, flags );
6417             EditorsValueWasNotModified();
6418         }
6419 
6420         return res;
6421     }
6422 
6423     return true;
6424 }
6425 
6426 // -----------------------------------------------------------------------
6427 
PerformValidation(wxPGProperty * p,wxVariant & pendingValue)6428 bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue )
6429 {
6430     //
6431     // Runs all validation functionality.
6432     // Returns true if value passes all tests.
6433     //
6434     //wxLogDebug(wxT("wxPropertyGrid::PerformValidation(p=%s)"),p->GetName().c_str());
6435 
6436     m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior;
6437     m_validationInfo.m_isFailing = true;
6438 
6439     if ( !wxPGIsVariantType(pendingValue, list) )
6440     {
6441         if ( !p->ActualValidateValue(pendingValue, m_validationInfo) )
6442             return false;
6443     }
6444 
6445     //
6446     // Adapt list to child values, if necessary
6447     wxVariant listValue = pendingValue;
6448     wxVariant* pPendingValue = &pendingValue;
6449     wxVariant* pList = NULL;
6450 
6451     // If parent has wxPG_PROP_AGGREGATE flag, or uses composite
6452     // string value, then we need treat as it was changed instead
6453     // (or, in addition, as is the case with composite string parent).
6454     // This includes creating list variant for child values.
6455 
6456     wxPGProperty* pwc = p->GetParent();
6457     wxPGProperty* changedProperty = p;
6458     wxPGProperty* baseChangedProperty = changedProperty;
6459     wxVariant bcpPendingList;
6460 
6461     listValue = pendingValue;
6462     listValue.SetName(p->GetBaseName());
6463 
6464     while ( pwc &&
6465             (pwc->HasFlag(wxPG_PROP_AGGREGATE) || pwc->HasFlag(wxPG_PROP_COMPOSED_VALUE)) )
6466     {
6467         wxVariantList tempList;
6468         wxVariant lv(tempList, pwc->GetBaseName());
6469         lv.Append(listValue);
6470         listValue = lv;
6471         pPendingValue = &listValue;
6472 
6473         if ( pwc->HasFlag(wxPG_PROP_AGGREGATE) )
6474         {
6475             baseChangedProperty = pwc;
6476             bcpPendingList = lv;
6477         }
6478 
6479         changedProperty = pwc;
6480         pwc = pwc->GetParent();
6481     }
6482 
6483     //wxLogDebug(wxT("wxPropertyGrid::PerformValidation(changedProperty=%s)"),changedProperty->GetName().c_str());
6484     //wxLogDebug(wxT("wxPropertyGrid::PerformValidation(baseChangedProperty=%s)"),baseChangedProperty->GetName().c_str());
6485 
6486     wxVariant value;
6487     wxPGProperty* evtChangingProperty = changedProperty;
6488 
6489     if ( !wxPGIsVariantType(*pPendingValue, list) )
6490     {
6491         value = *pPendingValue;
6492     }
6493     else
6494     {
6495         // Convert list to child values
6496         pList = pPendingValue;
6497         changedProperty->AdaptListToValue( *pPendingValue, &value );
6498     }
6499 
6500     wxVariant evtChangingValue = value;
6501 
6502     // FIXME: After proper ValueToString()s added, remove
6503     // this. It is just a temporary fix, as evt_changing
6504     // will simply not work for wxPG_PROP_COMPOSED_VALUE
6505     // (unless it is selected, and textctrl editor is open).
6506     if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
6507     {
6508         evtChangingProperty = baseChangedProperty;
6509         if ( evtChangingProperty != p )
6510         {
6511             evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue );
6512         }
6513         else
6514         {
6515             evtChangingValue = pendingValue;
6516         }
6517     }
6518 
6519     if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
6520     {
6521         if ( changedProperty == GetSelection() )
6522         {
6523             wxWindow* editorCtrl = GetEditorControl();
6524             wxASSERT( editorCtrl->IsKindOf(CLASSINFO(wxTextCtrl)) );
6525             evtChangingValue = wxStaticCast(editorCtrl, wxTextCtrl)->GetValue();
6526         }
6527         else
6528         {
6529             wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value."));
6530         }
6531     }
6532 
6533     wxASSERT( m_chgInfo_changedProperty == NULL );
6534     m_chgInfo_changedProperty = changedProperty;
6535     m_chgInfo_baseChangedProperty = baseChangedProperty;
6536     m_chgInfo_pendingValue = value;
6537 
6538     if ( pList )
6539         m_chgInfo_valueList = *pList;
6540     else
6541         m_chgInfo_valueList.MakeNull();
6542 
6543     // If changedProperty is not property which value was edited,
6544     // then call wxPGProperty::ValidateValue() for that as well.
6545     if ( p != changedProperty && !wxPGIsVariantType(value, list) )
6546     {
6547         if ( !changedProperty->ActualValidateValue(value, m_validationInfo) )
6548             return false;
6549     }
6550 
6551     // SendEvent returns true if event was vetoed
6552     if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue ) )
6553         return false;
6554 
6555     m_validationInfo.m_isFailing = false;
6556     return true;
6557 }
6558 
6559 // -----------------------------------------------------------------------
6560 
6561 #if wxUSE_STATUSBAR
GetStatusBar()6562 wxStatusBar* wxPropertyGrid::GetStatusBar()
6563 {
6564     wxWindow* topWnd = ::wxGetTopLevelParent(this);
6565     if ( topWnd && topWnd->IsKindOf(CLASSINFO(wxFrame)) )
6566     {
6567         wxFrame* pFrame = wxStaticCast(topWnd, wxFrame);
6568         if ( pFrame )
6569             return pFrame->GetStatusBar();
6570     }
6571     return NULL;
6572 }
6573 #endif
6574 
6575 // -----------------------------------------------------------------------
6576 
DoShowPropertyError(wxPGProperty * WXUNUSED (property),const wxString & msg)6577 void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), const wxString& msg )
6578 {
6579     if ( !msg.length() )
6580         return;
6581 
6582 #if wxUSE_STATUSBAR
6583     if ( !wxPGGlobalVars->m_offline )
6584     {
6585         wxStatusBar* pStatusBar = GetStatusBar();
6586         if ( pStatusBar )
6587         {
6588             pStatusBar->SetStatusText(msg);
6589             return;
6590         }
6591     }
6592 #endif
6593 
6594     ::wxMessageBox(msg, _("Property Error"));
6595 }
6596 
6597 // -----------------------------------------------------------------------
6598 
DoHidePropertyError(wxPGProperty * WXUNUSED (property))6599 void wxPropertyGrid::DoHidePropertyError( wxPGProperty* WXUNUSED(property) )
6600 {
6601 #if wxUSE_STATUSBAR
6602     if ( !wxPGGlobalVars->m_offline )
6603     {
6604         wxStatusBar* pStatusBar = GetStatusBar();
6605         if ( pStatusBar )
6606         {
6607             pStatusBar->SetStatusText(wxEmptyString);
6608             return;
6609         }
6610     }
6611 #endif
6612 }
6613 
6614 // -----------------------------------------------------------------------
6615 
OnValidationFailure(wxPGProperty * property,wxVariant & invalidValue)6616 bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property,
6617                                           wxVariant& invalidValue )
6618 {
6619     if ( m_inOnValidationFailure )
6620         return true;
6621 
6622     m_inOnValidationFailure = true;
6623 
6624     wxWindow* editor = GetEditorControl();
6625     int vfb = m_validationInfo.m_failureBehavior;
6626 
6627     if ( m_inDoSelectProperty )
6628     {
6629         // When property selection is being changed, do not display any
6630         // messages, if some were already shown for this property.
6631         if ( property->HasFlag(wxPG_PROP_INVALID_VALUE) )
6632         {
6633             m_validationInfo.m_failureBehavior =
6634                 vfb & ~(wxPG_VFB_SHOW_MESSAGE |
6635                         wxPG_VFB_SHOW_MESSAGEBOX |
6636                         wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR);
6637         }
6638     }
6639 
6640     // First call property's handler
6641     property->OnValidationFailure(invalidValue);
6642 
6643     bool res = DoOnValidationFailure(property, invalidValue);
6644 
6645     //
6646     // For non-wxTextCtrl editors, we do need to revert the value
6647     if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) &&
6648          property == GetSelection() )
6649     {
6650         property->GetEditorClass()->UpdateControl(property, editor);
6651     }
6652 
6653     property->SetFlag(wxPG_PROP_INVALID_VALUE);
6654 
6655     m_validationInfo.m_failureBehavior = vfb;
6656     m_inOnValidationFailure = false;
6657 
6658     return res;
6659 }
6660 
6661 // -----------------------------------------------------------------------
6662 
DoOnValidationFailure(wxPGProperty * property,wxVariant & WXUNUSED (invalidValue))6663 bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& WXUNUSED(invalidValue) )
6664 {
6665     int vfb = m_validationInfo.m_failureBehavior;
6666 
6667     if ( vfb & wxPG_VFB_BEEP )
6668         ::wxBell();
6669 
6670     if ( (vfb & wxPG_VFB_MARK_CELL) &&
6671          !property->HasFlag(wxPG_PROP_INVALID_VALUE) )
6672     {
6673         if ( !property->GetCell(0) && !property->GetCell(1) )
6674         {
6675             wxColour vfbFg = *wxWHITE;
6676             wxColour vfbBg = *wxRED;
6677             property->SetCell(0, new wxPGCell(property->GetLabel(), wxNullBitmap, vfbFg, vfbBg));
6678             property->SetCell(1, new wxPGCell(property->GetDisplayedString(), wxNullBitmap, vfbFg, vfbBg));
6679 
6680             DrawItemAndChildren(property);
6681 
6682             if ( property == GetSelection() )
6683             {
6684                 SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
6685 
6686                 wxWindow* editor = GetEditorControl();
6687                 if ( editor )
6688                 {
6689                     editor->SetForegroundColour(vfbFg);
6690                     editor->SetBackgroundColour(vfbBg);
6691                 }
6692             }
6693         }
6694     }
6695 
6696     if ( vfb & (wxPG_VFB_SHOW_MESSAGE |
6697                 wxPG_VFB_SHOW_MESSAGEBOX |
6698                 wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR) )
6699     {
6700         wxString msg = m_validationInfo.m_failureMessage;
6701 
6702         if ( !msg.length() )
6703             msg = _("You have entered invalid value. Press ESC to cancel editing.");
6704 
6705     #if wxUSE_STATUSBAR
6706         if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR )
6707         {
6708             if ( !wxPGGlobalVars->m_offline )
6709             {
6710                 wxStatusBar* pStatusBar = GetStatusBar();
6711                 if ( pStatusBar )
6712                     pStatusBar->SetStatusText(msg);
6713             }
6714         }
6715     #endif
6716 
6717         if ( vfb & wxPG_VFB_SHOW_MESSAGE )
6718             DoShowPropertyError(property, msg);
6719 
6720         if ( vfb & wxPG_VFB_SHOW_MESSAGEBOX )
6721             ::wxMessageBox(msg, _("Property Error"));
6722     }
6723 
6724     return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true;
6725 }
6726 
6727 // -----------------------------------------------------------------------
6728 
DoOnValidationFailureReset(wxPGProperty * property)6729 void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property )
6730 {
6731     int vfb = m_validationInfo.m_failureBehavior;
6732 
6733     if ( vfb & wxPG_VFB_MARK_CELL )
6734     {
6735         property->SetCell(0, NULL);
6736         property->SetCell(1, NULL);
6737 
6738         ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
6739 
6740         if ( property == GetSelection() && GetEditorControl() )
6741         {
6742             // Calling this will recreate the control, thus resetting its colour
6743             RefreshProperty(property);
6744         }
6745         else
6746         {
6747             DrawItemAndChildren(property);
6748         }
6749     }
6750 
6751 #if wxUSE_STATUSBAR
6752     if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR )
6753     {
6754         if ( !wxPGGlobalVars->m_offline )
6755         {
6756             wxStatusBar* pStatusBar = GetStatusBar();
6757             if ( pStatusBar )
6758                 pStatusBar->SetStatusText(wxEmptyString);
6759         }
6760     }
6761 #endif
6762 
6763     if ( vfb & wxPG_VFB_SHOW_MESSAGE )
6764     {
6765         DoHidePropertyError(property);
6766     }
6767 
6768     m_validationInfo.m_isFailing = false;
6769 }
6770 
6771 // -----------------------------------------------------------------------
6772 
SetValidationFailureBehavior(int vfbFlags)6773 void wxPropertyGridInterface::SetValidationFailureBehavior( int vfbFlags )
6774 {
6775     GetPropertyGrid()->m_permanentValidationFailureBehavior = vfbFlags;
6776 }
6777 
6778 // -----------------------------------------------------------------------
6779 
6780 // flags are same as with DoSelectProperty
DoPropertyChanged(wxPGProperty * p,unsigned int selFlags)6781 bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags )
6782 {
6783     if ( m_inDoPropertyChanged )
6784         return true;
6785 
6786     wxPGProperty* selected = GetSelection();
6787 
6788     m_pState->m_anyModified = 1;
6789 
6790     m_inDoPropertyChanged = 1;
6791 
6792     // If property's value is being changed, assume it is valid
6793     OnValidationFailureReset(selected);
6794 
6795     // Maybe need to update control
6796     wxASSERT( m_chgInfo_changedProperty != NULL );
6797 
6798     // These values were calculated in PerformValidation()
6799     wxPGProperty* changedProperty = m_chgInfo_changedProperty;
6800     wxVariant value = m_chgInfo_pendingValue;
6801 
6802     wxPGProperty* topPaintedProperty = changedProperty;
6803 
6804     while ( !topPaintedProperty->IsCategory() &&
6805             !topPaintedProperty->IsRoot() )
6806     {
6807         topPaintedProperty = topPaintedProperty->GetParent();
6808     }
6809 
6810     changedProperty->SetValue(value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER);
6811 
6812     // NB: Call GetEditorControl() as late as possible, because OnSetValue()
6813     //     and perhaps other user-defined virtual functions may change it.
6814     wxWindow* editor = GetEditorControl();
6815 
6816     // Set as Modified (not if dragging just began)
6817     if ( !(p->m_flags & wxPG_PROP_MODIFIED) )
6818     {
6819         p->m_flags |= wxPG_PROP_MODIFIED;
6820         if ( p == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
6821         {
6822             if ( editor )
6823                 SetCurControlBoldFont();
6824         }
6825     }
6826 
6827     wxPGProperty* pwc;
6828 
6829     // Propagate updates to parent(s)
6830     pwc = p;
6831     wxPGProperty* prevPwc = NULL;
6832 
6833     while ( prevPwc != topPaintedProperty )
6834     {
6835         pwc->m_flags |= wxPG_PROP_MODIFIED;
6836 
6837         if ( pwc == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
6838         {
6839             if ( editor )
6840                 SetCurControlBoldFont();
6841         }
6842 
6843         prevPwc = pwc;
6844         pwc = pwc->GetParent();
6845     }
6846 
6847     // Draw the actual property
6848     DrawItemAndChildren( topPaintedProperty );
6849 
6850     //
6851     // If value was set by wxPGProperty::OnEvent, then update the editor
6852     // control.
6853     if ( selFlags & wxPG_SEL_DIALOGVAL )
6854     {
6855         RefreshEditor();
6856     }
6857     else
6858     {
6859 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
6860         if ( m_wndEditor ) m_wndEditor->Refresh();
6861         if ( m_wndEditor2 ) m_wndEditor2->Refresh();
6862 #endif
6863     }
6864 
6865     // Sanity check
6866     wxASSERT( !changedProperty->GetParent()->HasFlag(wxPG_PROP_AGGREGATE) );
6867 
6868     // If top parent has composite string value, then send to child parents,
6869     // starting from baseChangedProperty.
6870     if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
6871     {
6872         pwc = m_chgInfo_baseChangedProperty;
6873 
6874         while ( pwc != changedProperty )
6875         {
6876             SendEvent( wxEVT_PG_CHANGED, pwc, NULL );
6877 
6878             pwc = pwc->GetParent();
6879         }
6880     }
6881 
6882     SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL );
6883 
6884     m_inDoPropertyChanged = 0;
6885 
6886     return true;
6887 }
6888 
6889 // -----------------------------------------------------------------------
6890 
ChangePropertyValue(wxPGPropArg id,wxVariant newValue)6891 bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id, wxVariant newValue )
6892 {
6893     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
6894 
6895     m_chgInfo_changedProperty = NULL;
6896 
6897     if ( PerformValidation(p, newValue) )
6898     {
6899         DoPropertyChanged(p);
6900         return true;
6901     }
6902     else
6903     {
6904         OnValidationFailure(p, newValue);
6905     }
6906 
6907     return false;
6908 }
6909 
6910 // -----------------------------------------------------------------------
6911 
6912 // Runs wxValidator for the selected property
DoEditorValidate()6913 bool wxPropertyGrid::DoEditorValidate()
6914 {
6915 #if wxUSE_VALIDATORS
6916     // With traditional validator style, we dont need to more
6917     if ( !(GetExtraStyle() & wxPG_EX_LEGACY_VALIDATORS) )
6918         return true;
6919 
6920     if ( m_iFlags & wxPG_FL_VALIDATION_FAILED )
6921     {
6922         return false;
6923     }
6924 
6925     wxWindow* wnd = GetEditorControl();
6926     wxPGProperty* selected = GetSelection();
6927     wxValidator* validator = NULL;
6928 
6929     if ( selected )
6930         validator = selected->GetValidator();
6931 
6932     if ( validator && wnd )
6933     {
6934         // Use TextCtrl of ODComboBox instead
6935         if ( wnd->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
6936         {
6937             wnd = ((wxPGOwnerDrawnComboBox*)wnd)->GetTextCtrl();
6938 
6939             if ( !wnd )
6940                 return true;
6941         }
6942 
6943         validator->SetWindow(wnd);
6944 
6945         // Instead setting the flag after the failure, we set
6946         // it before checking and then clear afterwards if things
6947         // went fine. This trick is necessary since focus events
6948         // may be triggered while in Validate.
6949         m_iFlags |= wxPG_FL_VALIDATION_FAILED;
6950         if ( !validator->Validate(this) )
6951         {
6952             // If you dpm't want to display message multiple times per change,
6953             // comment the following line.
6954             m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
6955             return false;
6956         }
6957         m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
6958     }
6959 #endif
6960     return true;
6961 }
6962 
6963 // -----------------------------------------------------------------------
6964 
ProcessEvent(wxEvent & event)6965 bool wxPropertyGrid::ProcessEvent(wxEvent& event)
6966 {
6967     wxWindow* wnd = (wxWindow*) event.GetEventObject();
6968     if ( wnd && wnd->IsKindOf(CLASSINFO(wxWindow)) )
6969     {
6970         wxWindow* parent = wnd->GetParent();
6971 
6972          // JACS: only look at command events
6973         if ( event.IsCommandEvent() && parent &&
6974              (parent == m_canvas ||
6975               parent->GetParent() == m_canvas) )
6976         {
6977             // JACS: don't return here, fall through to ProcessEvent
6978             if ( HandleCustomEditorEvent((wxCommandEvent&)event) )
6979                 return true;
6980         }
6981     }
6982     return wxPanel::ProcessEvent(event);
6983 }
6984 
6985 // -----------------------------------------------------------------------
6986 
OnCustomEditorEvent(wxCommandEvent & event)6987 void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event )
6988 {
6989     HandleCustomEditorEvent(event);
6990 }
6991 
6992 // -----------------------------------------------------------------------
6993 
6994 // NB: It may really not be wxCommandEvent - must check if necessary
6995 //     (usually not).
HandleCustomEditorEvent(wxCommandEvent & event)6996 bool wxPropertyGrid::HandleCustomEditorEvent( wxCommandEvent &event )
6997 {
6998     // It is possible that this handler receives event even before
6999     // the control has been properly initialized. Let's skip the
7000     // handling in that case.
7001     if ( !m_pState )
7002         return false;
7003 
7004     // Don't care about the event if it originated from the
7005     // 'label editor'. In this function we only care about the
7006     // property value editor.
7007     if ( m_labelEditor && event.GetId() == m_labelEditor->GetId() )
7008     {
7009         event.Skip();
7010         return false;
7011     }
7012 
7013     wxPGProperty* selected = GetSelection();
7014 
7015     //
7016     // Somehow, event is handled after property has been deselected.
7017     // Possibly, but very rare.
7018     if ( !selected || selected->HasFlag(wxPG_PROP_BEING_DELETED) )
7019         return false;
7020 
7021     if ( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT ||
7022          m_inDoSelectProperty ||
7023          m_inOnValidationFailure ||
7024          // Also don't handle editor event if wxEVT_PG_CHANGED or
7025          // similar is currently doing something (showing a
7026          // message box, for instance).
7027          m_processedEvent )
7028         return false;
7029 
7030     wxVariant pendingValue(selected->GetValueRef());
7031     wxWindow* wnd = GetEditorControl();
7032     wxWindow* editorWnd = wxDynamicCast(event.GetEventObject(), wxWindow);
7033     int selFlags = 0;
7034     bool wasUnspecified = selected->IsValueUnspecified();
7035     int usesAutoUnspecified = selected->UsesAutoUnspecified();
7036 
7037     bool valueIsPending = false;
7038 
7039     m_chgInfo_changedProperty = NULL;
7040 
7041     m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED|wxPG_FL_VALUE_CHANGE_IN_EVENT);
7042 
7043     //
7044     // Filter out excess wxTextCtrl modified events
7045     if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd )
7046     {
7047         if ( wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
7048         {
7049             wxTextCtrl* tc = (wxTextCtrl*) wnd;
7050 
7051             wxString newTcValue = tc->GetValue();
7052             if ( m_prevTcValue == newTcValue )
7053                 return false;
7054             m_prevTcValue = newTcValue;
7055         }
7056         else if ( wnd->IsKindOf(CLASSINFO(wxPGComboCtrl)) )
7057         {
7058             wxPGComboCtrl* cc = (wxPGComboCtrl*) wnd;
7059 
7060             wxString newTcValue = cc->GetTextCtrl()->GetValue();
7061             if ( m_prevTcValue == newTcValue )
7062                 return false;
7063             m_prevTcValue = newTcValue;
7064         }
7065     }
7066 
7067     SetInternalFlag(wxPG_FL_IN_ONCUSTOMEDITOREVENT);
7068 
7069     bool validationFailure = false;
7070     bool buttonWasHandled = false;
7071 
7072     //
7073     // Try common button handling
7074     if ( m_wndEditor2 && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
7075     {
7076         wxPGEditorDialogAdapter* adapter = selected->GetEditorDialog();
7077 
7078         if ( adapter )
7079         {
7080             buttonWasHandled = true;
7081             // Store as res2, as previously (and still currently alternatively)
7082             // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED
7083             // in wxPGProperty::OnEvent().
7084             adapter->ShowDialog( this, selected );
7085             delete adapter;
7086         }
7087     }
7088 
7089     if ( !buttonWasHandled )
7090     {
7091         if ( wnd || m_wndEditor2 )
7092         {
7093             // First call editor class' event handler.
7094             const wxPGEditor* editor = selected->GetEditorClass();
7095 
7096             if ( editor->OnEvent( this, selected, editorWnd, event ) )
7097             {
7098                 // If changes, validate them
7099                 if ( DoEditorValidate() )
7100                 {
7101                     if ( editor->ActualGetValueFromControl( pendingValue,
7102                                                             selected,
7103                                                             wnd ) )
7104                         valueIsPending = true;
7105 
7106                     // Mark value always as pending if validation is currently
7107                     // failing and value was not unspecified
7108                     if ( !valueIsPending &&
7109                          !pendingValue.IsNull() &&
7110                          m_validationInfo.m_isFailing )
7111                          valueIsPending = true;
7112                 }
7113                 else
7114                 {
7115                     validationFailure = true;
7116                 }
7117             }
7118         }
7119 
7120         // Then the property's custom handler (must be always called, unless
7121         // validation failed).
7122         if ( !validationFailure )
7123             buttonWasHandled = selected->OnEvent( this, editorWnd, event );
7124     }
7125 
7126     // SetValueInEvent(), as called in one of the functions referred above
7127     // overrides editor's value.
7128     if ( m_iFlags & wxPG_FL_VALUE_CHANGE_IN_EVENT )
7129     {
7130         valueIsPending = true;
7131         pendingValue = m_changeInEventValue;
7132         selFlags |= wxPG_SEL_DIALOGVAL;
7133     }
7134 
7135     if ( !validationFailure && valueIsPending )
7136         if ( !PerformValidation(selected, pendingValue) )
7137             validationFailure = true;
7138 
7139     if ( validationFailure )
7140     {
7141         OnValidationFailure(selected, pendingValue);
7142     }
7143     else if ( valueIsPending )
7144     {
7145         selFlags |= ( !wasUnspecified && selected->IsValueUnspecified() && usesAutoUnspecified ) ? wxPG_SEL_SETUNSPEC : 0;
7146 
7147         DoPropertyChanged(selected, selFlags);
7148         EditorsValueWasNotModified();
7149 
7150         // Unfocus on enter press?
7151         if ( GetExtraStyle() & wxPG_EX_UNFOCUS_ON_ENTER && event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER)
7152         {
7153            UnfocusEditor();
7154         }
7155     }
7156     else
7157     {
7158         // No value after all
7159 
7160         // Let unhandled button click events go to the parent
7161         if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
7162         {
7163             wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
7164             GetEventHandler()->AddPendingEvent(evt);
7165             buttonWasHandled = true;
7166         }
7167     }
7168 
7169     ClearInternalFlag(wxPG_FL_IN_ONCUSTOMEDITOREVENT);
7170     return buttonWasHandled;
7171 }
7172 
7173 // -----------------------------------------------------------------------
7174 // wxPropertyGrid editor control helper methods
7175 // -----------------------------------------------------------------------
7176 
GetEditorWidgetRect(wxPGProperty * p,int column) const7177 wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const
7178 {
7179     int itemy = p->GetY2(m_lineHeight);
7180     int vy = 0;
7181     int splitterX = m_pState->DoGetSplitterPosition(column-1);
7182     int colEnd = splitterX + m_pState->m_colWidths[column];
7183     int imageOffset = 0;
7184 
7185     if ( column == 1 )
7186     {
7187         // TODO: If custom image detection changes from current, change this.
7188         if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
7189         {
7190             //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
7191             int iw = p->OnMeasureImage().x;
7192             if ( iw < 1 )
7193                 iw = wxPG_CUSTOM_IMAGE_WIDTH;
7194             imageOffset = p->GetImageOffset(iw);
7195         }
7196     }
7197     else if ( column == 0 )
7198     {
7199         splitterX += (p->m_depth-1) * m_subgroup_extramargin;
7200     }
7201 
7202     return wxRect
7203       (
7204         splitterX+imageOffset+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1,
7205         itemy-vy,
7206         colEnd-splitterX-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-imageOffset-1,
7207         m_lineHeight-1
7208       );
7209 }
7210 
7211 // -----------------------------------------------------------------------
7212 
GetImageRect(wxPGProperty * p,int item) const7213 wxRect wxPropertyGrid::GetImageRect( wxPGProperty* p, int item ) const
7214 {
7215     wxSize sz = GetImageSize(p, item);
7216     return wxRect(wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
7217                   wxPG_CUSTOM_IMAGE_SPACINGY,
7218                   sz.x,
7219                   sz.y);
7220 }
7221 
7222 // return size of custom paint image
GetImageSize(wxPGProperty * p,int item) const7223 wxSize wxPropertyGrid::GetImageSize( wxPGProperty* p, int item ) const
7224 {
7225     // If called with NULL property, then return default image
7226     // size for properties that use image.
7227     if ( !p )
7228         return wxSize(wxPG_CUSTOM_IMAGE_WIDTH,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight));
7229 
7230     wxSize cis = p->OnMeasureImage(item);
7231 
7232     int choiceCount = p->GetChoiceCount();
7233     int comVals = p->GetDisplayedCommonValueCount();
7234     if ( item >= choiceCount && comVals > 0 )
7235     {
7236         unsigned int cvi = item-choiceCount;
7237         cis = GetCommonValue(cvi)->GetRenderer()->GetImageSize(NULL, 1, cvi);
7238     }
7239     else if ( item >= 0 && choiceCount == 0 )
7240         return wxSize(0, 0);
7241 
7242     if ( cis.x < 0 )
7243     {
7244         if ( cis.x <= -1 )
7245             cis.x = wxPG_CUSTOM_IMAGE_WIDTH;
7246     }
7247     if ( cis.y <= 0 )
7248     {
7249         if ( cis.y >= -1 )
7250             cis.y = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
7251         else
7252             cis.y = -cis.y;
7253     }
7254     return cis;
7255 }
7256 
7257 // -----------------------------------------------------------------------
7258 
7259 // takes scrolling into account
ImprovedClientToScreen(int * px,int * py)7260 void wxPropertyGrid::ImprovedClientToScreen( int* px, int* py )
7261 {
7262     int vx, vy;
7263     GetViewStart(&vx,&vy);
7264     vy*=wxPG_PIXELS_PER_UNIT;
7265     vx*=wxPG_PIXELS_PER_UNIT;
7266     *px -= vx;
7267     *py -= vy;
7268     ClientToScreen( px, py );
7269 }
7270 
7271 // -----------------------------------------------------------------------
7272 
7273 // custom set cursor
CustomSetCursor(int type,bool override)7274 void wxPropertyGrid::CustomSetCursor( int type, bool override )
7275 {
7276     if ( type == m_curcursor && !override ) return;
7277 
7278     wxCursor* cursor = &wxPG_DEFAULT_CURSOR;
7279 
7280     if ( type == wxCURSOR_SIZEWE )
7281         cursor = m_cursorSizeWE;
7282 
7283     m_canvas->SetCursor( *cursor );
7284 
7285     m_curcursor = type;
7286 }
7287 
7288 // -----------------------------------------------------------------------
7289 
GetUnspecifiedValueText(int argFlags) const7290 wxString wxPropertyGrid::GetUnspecifiedValueText( int argFlags ) const
7291 {
7292     const wxPGCell& ua = GetUnspecifiedValueAppearance();
7293 
7294     if ( ua.HasText() &&
7295          !(argFlags & wxPG_FULL_VALUE) &&
7296          !(argFlags & wxPG_EDITABLE_VALUE) )
7297         return ua.GetText();
7298 
7299     return wxEmptyString;
7300 }
7301 
7302 // -----------------------------------------------------------------------
7303 // wxPropertyGrid property selection
7304 // -----------------------------------------------------------------------
7305 
7306 #define CONNECT_CHILD(EVT,FUNCTYPE,FUNC) \
7307     wnd->Connect(id, EVT, \
7308         (wxObjectEventFunction) (wxEventFunction)  \
7309         FUNCTYPE (&wxPropertyGrid::FUNC), \
7310         NULL, this );
7311 
7312 // Setups event handling for child control
SetupEventHandling(wxWindow * argWnd,int id)7313 void wxPropertyGrid::SetupEventHandling( wxWindow* argWnd, int id )
7314 {
7315     wxWindow* wnd = argWnd;
7316 
7317     if ( argWnd == m_wndEditor )
7318     {
7319         CONNECT_CHILD(wxEVT_MOTION,(wxMouseEventFunction),OnMouseMoveChild)
7320         CONNECT_CHILD(wxEVT_LEFT_UP,(wxMouseEventFunction),OnMouseUpChild)
7321         CONNECT_CHILD(wxEVT_LEFT_DOWN,(wxMouseEventFunction),OnMouseClickChild)
7322         CONNECT_CHILD(wxEVT_RIGHT_UP,(wxMouseEventFunction),OnMouseRightClickChild)
7323         CONNECT_CHILD(wxEVT_ENTER_WINDOW,(wxMouseEventFunction),OnMouseEntry)
7324         CONNECT_CHILD(wxEVT_LEAVE_WINDOW,(wxMouseEventFunction),OnMouseEntry)
7325     }
7326     else
7327     {
7328         CONNECT_CHILD(wxEVT_NAVIGATION_KEY,(wxNavigationKeyEventFunction),OnNavigationKey)
7329     }
7330     CONNECT_CHILD(wxEVT_KEY_DOWN,(wxCharEventFunction),OnChildKeyDown)
7331     CONNECT_CHILD(wxEVT_KEY_UP,(wxCharEventFunction),OnChildKeyUp)
7332     CONNECT_CHILD(wxEVT_KILL_FOCUS,(wxFocusEventFunction),OnFocusEvent)
7333 }
7334 
DestroyEditorWnd(wxWindow * wnd)7335 void wxPropertyGrid::DestroyEditorWnd( wxWindow* wnd )
7336 {
7337     if ( !wnd )
7338         return;
7339 
7340     wxPendingDelete.Append(wnd);
7341     wnd->Hide();
7342 }
7343 
FreeEditors()7344 void wxPropertyGrid::FreeEditors()
7345 {
7346     //
7347     // Return focus back to canvas from children (this is required at least for
7348     // GTK+, which, unlike Windows, clears focus when control is destroyed
7349     // instead of moving it to closest parent).
7350     wxWindow* focus = wxWindow::FindFocus();
7351     if ( focus )
7352     {
7353         wxWindow* parent = focus->GetParent();
7354         while ( parent )
7355         {
7356             if ( parent == m_canvas )
7357             {
7358                 SetFocusOnCanvas();
7359                 break;
7360             }
7361             parent = parent->GetParent();
7362         }
7363     }
7364 
7365     DestroyEditorWnd(m_wndEditor2);
7366     m_wndEditor2 = NULL;
7367 
7368     DestroyEditorWnd(m_wndEditor);
7369     m_wndEditor = NULL;
7370 }
7371 
7372 // Call with NULL to de-select property
DoSelectProperty(wxPGProperty * p,unsigned int flags)7373 bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
7374 {
7375     /*
7376     if (p)
7377         wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(),
7378             p->m_parent->m_label.c_str(),p->GetIndexInParent());
7379     else
7380         wxLogDebug(wxT("SelectProperty( NULL, -1 )"));
7381     */
7382 
7383     if ( m_inDoSelectProperty )
7384         return true;
7385 
7386     m_inDoSelectProperty = true;
7387 /* C::B begin */
7388     struct UnsetValue
7389     {
7390         UnsetValue(bool &value) : m_value(value) {}
7391         ~UnsetValue() { m_value = false; }
7392         bool &m_value;
7393     };
7394     UnsetValue unsetValue(m_inDoSelectProperty);
7395 /* C::B end */
7396 
7397     if ( !m_pState )
7398         return false;
7399 
7400     wxArrayPGProperty prevSelection = m_pState->m_selection;
7401     wxPGProperty* prevFirstSel;
7402 
7403     if ( prevSelection.size() > 0 )
7404         prevFirstSel = prevSelection[0];
7405     else
7406         prevFirstSel = NULL;
7407 
7408     if ( prevFirstSel && prevFirstSel->HasFlag(wxPG_PROP_BEING_DELETED) )
7409         prevFirstSel = NULL;
7410 
7411     // Always send event, as this is indirect call
7412     DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
7413 
7414     wxWindow* primaryCtrl = NULL;
7415 
7416     //
7417     // If we are frozen, then just set the values.
7418     if ( m_frozen )
7419     {
7420         m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
7421         m_editorFocused = 0;
7422         m_pState->DoSetSelection(p);
7423         m_selColumn = 1;
7424 
7425         // If frozen, always free controls. But don't worry, as Thaw will
7426         // recall SelectProperty to recreate them.
7427         FreeEditors();
7428 
7429         // Prevent any further selection measures in this call
7430         p = (wxPGProperty*) NULL;
7431     }
7432     else
7433     {
7434         // Is it the same?
7435         if ( prevFirstSel == p &&
7436              prevSelection.size() <= 1 &&
7437              !(flags & wxPG_SEL_FORCE) )
7438         {
7439             // Only set focus if not deselecting
7440             if ( p )
7441             {
7442                 if ( flags & wxPG_SEL_FOCUS )
7443                 {
7444                     if ( m_wndEditor )
7445                     {
7446                         m_wndEditor->SetFocus();
7447                         m_editorFocused = 1;
7448                     }
7449                 }
7450                 else
7451                 {
7452                     SetFocusOnCanvas();
7453                 }
7454             }
7455             return true;
7456         }
7457 
7458         //
7459         // First, deactivate previous
7460         if ( prevFirstSel )
7461         {
7462             // Must double-check if this is an selected in case of forceswitch
7463             if ( p != prevFirstSel )
7464             {
7465                 if ( !CommitChangesFromEditor(flags) )
7466                 {
7467                     // Validation has failed, so we can't exit the previous editor
7468                     //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."),
7469                     //               _("Invalid Value"),wxOK|wxICON_ERROR);
7470                     return false;
7471                 }
7472             }
7473 
7474             // This should be called after CommitChangesFromEditor(), so that
7475             // OnValidationFailure() still has information on property's
7476             // validation state.
7477             OnValidationFailureReset(prevFirstSel);
7478 
7479             FreeEditors();
7480 
7481             m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
7482             EditorsValueWasNotModified();
7483         }
7484 
7485         SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
7486 
7487         m_pState->DoSetSelection(p);
7488 
7489         //
7490         // Redraw unselected
7491         for ( unsigned int i=0; i<prevSelection.size(); i++ )
7492         {
7493             DrawItem(prevSelection[i]);
7494         }
7495 
7496         //
7497         // Then, activate the one given.
7498 
7499         if ( p )
7500         {
7501             int propY = p->GetY2(m_lineHeight);
7502 
7503             int splitterX = GetSplitterPosition();
7504             m_editorFocused = 0;
7505             m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
7506             if ( p != prevFirstSel )
7507                 m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
7508 
7509             wxASSERT( m_wndEditor == (wxWindow*) NULL );
7510 
7511 
7512             //
7513             // Only create editor for non-disabled non-caption
7514             if ( !p->IsCategory() && !(p->m_flags & wxPG_PROP_DISABLED) )
7515             {
7516             // do this for non-caption items
7517 
7518                 m_selColumn = 1;
7519 
7520                 const wxPGEditor* editor = p->GetEditorClass();
7521                 wxCHECK_MSG(editor, false,
7522                     wxT("NULL editor class not allowed"));
7523 
7524                 // Do we need to paint the custom image, if any?
7525                 m_iFlags &= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE);
7526                 if ( (p->m_flags & wxPG_PROP_CUSTOMIMAGE) &&
7527                      !editor->CanContainCustomImage() )
7528                     m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
7529 
7530                 wxRect grect = GetEditorWidgetRect(p, m_selColumn);
7531                 wxPoint goodPos = grect.GetPosition();
7532 
7533                 // Reset editor appearance
7534                 wxPGCell emptyCell;
7535                 m_editorAppearance.Assign(emptyCell);
7536 
7537                 m_iFlags &= ~wxPG_FL_FIXED_WIDTH_EDITOR;
7538 
7539                 wxPGWindowList wndList = editor->CreateControls(this,
7540                                                                 p,
7541                                                                 goodPos,
7542                                                                 grect.GetSize());
7543 
7544                 //
7545                 // Below, bear in mind the difference between primaryCtrl and m_wndEditor:
7546                 // m_wndEditor is the actual wxWindow on canvas, and primaryCtrl is
7547                 // the actual editor control. They may different if wxPGClipperWindow is
7548                 // used for this editor.
7549                 //
7550                 m_wndEditor = wndList.m_primary;
7551                 m_wndEditor2 = wndList.m_secondary;
7552                 primaryCtrl = GetEditorControl();
7553 
7554                 // NOTE: It is allowed for m_wndEditor to be NULL - in this case
7555                 //       value is drawn as normal, and m_wndEditor2 is assumed
7556                 //       to be a right-aligned button that triggers a separate editor
7557                 //       window.
7558 
7559                 if ( m_wndEditor )
7560                 {
7561                     wxCHECK_MSG( m_wndEditor->GetParent() == m_canvas,
7562                                  false,
7563                                  wxT("CreateControls must use result of ")
7564                                  wxT("wxPropertyGrid::GetPanel() as parent ")
7565                                  wxT("of controls.") );
7566 
7567                     // Set validator, if any
7568                 #if wxUSE_VALIDATORS
7569                     if ( !(GetExtraStyle() & wxPG_EX_LEGACY_VALIDATORS) )
7570                     {
7571                         wxValidator* validator = p->GetValidator();
7572                         if ( validator )
7573                             primaryCtrl->SetValidator(*validator);
7574                     }
7575                 #endif
7576 
7577                     if ( m_wndEditor->GetSize().y > (m_lineHeight+6) )
7578                         m_iFlags |= wxPG_FL_ABNORMAL_EDITOR;
7579 
7580                     // If it has modified status, use bold font
7581                     // (must be done before capturing m_ctrlXAdjust)
7582                     if ( (p->m_flags & wxPG_PROP_MODIFIED) && (m_windowStyle & wxPG_BOLD_MODIFIED) )
7583                         SetCurControlBoldFont();
7584 
7585                     //
7586                     // Fix TextCtrl indentation
7587                     wxTextCtrl* tc = NULL;
7588                     if ( primaryCtrl->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
7589                         tc = ((wxPGOwnerDrawnComboBox*)primaryCtrl)->GetTextCtrl();
7590                     else
7591                         tc = wxDynamicCast(primaryCtrl, wxTextCtrl);
7592                     if ( tc )
7593                         wxPG_TextCtrl_SetMargins(tc, wxPoint(0, -1));
7594 
7595                     // Store x relative to splitter (we'll need it).
7596                     m_ctrlXAdjust = m_wndEditor->GetPosition().x - splitterX;
7597 
7598                     // Check if background clear is not necessary
7599                     wxPoint pos = m_wndEditor->GetPosition();
7600                     if ( pos.x > (splitterX+1) || pos.y > propY )
7601                     {
7602                         m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
7603                     }
7604 
7605                     m_wndEditor->SetSizeHints(3, 3);
7606                     if ( primaryCtrl != m_wndEditor )
7607                         primaryCtrl->SetSizeHints(3, 3);
7608 
7609                     SetupEventHandling(primaryCtrl, wxPG_SUBID1);
7610 
7611                     if ( p->IsValueUnspecified() )
7612                     {
7613                         editor->SetValueToUnspecified(p, primaryCtrl);
7614                         SetEditorAppearance(m_unspecifiedAppearance);
7615                     }
7616 
7617                     // Focus and select all (wxTextCtrl, wxComboBox etc)
7618                     if ( flags & wxPG_SEL_FOCUS )
7619                     {
7620                         primaryCtrl->SetFocus();
7621 
7622                         editor->OnFocus(p, primaryCtrl);
7623                     }
7624                 }
7625 
7626                 if ( m_wndEditor2 )
7627                 {
7628                     wxCHECK_MSG( m_wndEditor2->GetParent() == m_canvas,
7629                                  false,
7630                                  wxT("CreateControls must use result of ")
7631                                  wxT("wxPropertyGrid::GetPanel() as parent ")
7632                                  wxT("of controls.") );
7633 
7634                     // Get proper id for wndSecondary
7635                     m_wndSecId = m_wndEditor2->GetId();
7636                     wxWindowList children = m_wndEditor2->GetChildren();
7637                     wxWindowList::iterator node = children.begin();
7638                     if ( node != children.end() )
7639                         m_wndSecId = ((wxWindow*)*node)->GetId();
7640 
7641                     m_wndEditor2->SetSizeHints(3,3);
7642 
7643                     m_wndEditor2->Show();
7644 
7645                     SetupEventHandling(m_wndEditor2,wxPG_SUBID2);
7646 
7647                     // If no primary editor, focus to button to allow
7648                     // it to interprete ENTER etc.
7649                     // NOTE: Due to problems focusing away from it, this
7650                     //       has been disabled.
7651                     /*
7652                     if ( (flags & wxPG_SEL_FOCUS) && !m_wndEditor )
7653                         m_wndEditor2->SetFocus();
7654                     */
7655                 }
7656 
7657                 if ( flags & wxPG_SEL_FOCUS )
7658                     m_editorFocused = 1;
7659             }
7660             else
7661             {
7662                 // wxGTK atleast seems to need this (wxMSW not)
7663                 SetFocusOnCanvas();
7664             }
7665 
7666             EditorsValueWasNotModified();
7667 
7668             // If it's inside collapsed section, expand parent, scroll, etc.
7669             // Also, if it was partially visible, scroll it into view.
7670             if ( !(flags & wxPG_SEL_NONVISIBLE) )
7671                 EnsureVisible( p );
7672 
7673             if ( m_wndEditor )
7674             {
7675                 m_wndEditor->Show(true);
7676             }
7677         }
7678 
7679         if ( !(flags & wxPG_SEL_NO_REFRESH) )
7680             DrawItem(p);
7681 
7682         ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
7683     }
7684 
7685     const wxString* pHelpString = NULL;
7686 
7687     if ( p )
7688         pHelpString = &p->GetHelpString();
7689 
7690     if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) )
7691     {
7692 #if wxUSE_STATUSBAR
7693 
7694         //
7695         // Show help text in status bar.
7696         //   (if found and grid not embedded in manager with help box and
7697         //    style wxPG_EX_HELP_AS_TOOLTIPS is not used).
7698         //
7699         wxStatusBar* statusbar = GetStatusBar();
7700         if ( statusbar )
7701         {
7702             if ( pHelpString && pHelpString->length() )
7703             {
7704                 // Set help box text.
7705                 statusbar->SetStatusText( *pHelpString );
7706                 m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
7707             }
7708             else if ( m_iFlags & wxPG_FL_STRING_IN_STATUSBAR )
7709             {
7710                 // Clear help box - but only if it was written
7711                 // by us at previous time.
7712                 statusbar->SetStatusText( m_emptyString );
7713                 m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR);
7714             }
7715         }
7716 #endif
7717     }
7718     else
7719     {
7720 #if wxPG_SUPPORT_TOOLTIPS
7721         if ( pHelpString && pHelpString->length() &&
7722              primaryCtrl )
7723         {
7724             primaryCtrl->SetToolTip(*pHelpString);
7725         }
7726 #endif
7727     }
7728 
7729     // call wx event handler (here so that it also occurs on deselection)
7730     SendEvent( wxEVT_PG_SELECTED, p, NULL );
7731 
7732     return true;
7733 }
7734 
7735 // -----------------------------------------------------------------------
7736 
UnfocusEditor()7737 bool wxPropertyGrid::UnfocusEditor()
7738 {
7739     wxPGProperty* selected = GetSelection();
7740 
7741     if ( !selected || !m_wndEditor || m_frozen )
7742         return true;
7743 
7744     if ( !CommitChangesFromEditor(0) )
7745     {
7746         // Allow UnfocusEditor() to work when called from CommitChangesFromEditor()
7747         if ( !(m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT) )
7748             return false;
7749     }
7750 
7751     SetFocusOnCanvas();
7752     DrawItem(selected);
7753 
7754     return true;
7755 }
7756 
7757 // -----------------------------------------------------------------------
7758 // wxPropertyGrid expand/collapse state
7759 // -----------------------------------------------------------------------
7760 
DoCollapse(wxPGProperty * p,bool sendEvents)7761 bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents )
7762 {
7763     wxPGProperty* pwc = wxStaticCast(p, wxPGProperty);
7764 
7765     wxPGProperty* selected = GetSelection();
7766 
7767     // If active editor was inside collapsed section, then disable it
7768     if ( selected && selected->IsSomeParent (p) )
7769     {
7770         if ( !ClearSelection() )
7771             return false;
7772     }
7773 
7774     // Store dont-center-splitter flag 'cause we need to temporarily set it
7775     wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
7776     m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
7777 
7778     bool res = m_pState->DoCollapse(pwc);
7779 
7780     if ( res )
7781     {
7782         if ( sendEvents )
7783             SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
7784 
7785         RecalculateVirtualSize();
7786 
7787         // Redraw etc. only if collapsed was visible.
7788         if (pwc->IsVisible() &&
7789             !m_frozen &&
7790             ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) )
7791         {
7792             // When item is collapsed so that scrollbar would move,
7793             // graphics mess is about (unless we redraw everything).
7794             Refresh();
7795         }
7796     }
7797 
7798     // Clear dont-center-splitter flag if it wasn't set
7799     m_iFlags = (m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER)) | old_flag;
7800 
7801     return res;
7802 }
7803 
7804 // -----------------------------------------------------------------------
7805 
DoExpand(wxPGProperty * p,bool sendEvents)7806 bool wxPropertyGrid::DoExpand( wxPGProperty* p, bool sendEvents )
7807 {
7808     wxCHECK_MSG( p, false, wxT("invalid property id") );
7809 
7810     wxPGProperty* pwc = (wxPGProperty*)p;
7811 
7812     // Store dont-center-splitter flag 'cause we need to temporarily set it
7813     wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
7814     m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
7815 
7816     bool res = m_pState->DoExpand(pwc);
7817 
7818     if ( res )
7819     {
7820         if ( sendEvents )
7821             SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
7822 
7823         RecalculateVirtualSize();
7824 
7825         // Redraw etc. only if expanded was visible.
7826         if ( pwc->IsVisible() && !m_frozen &&
7827              ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
7828            )
7829         {
7830             // Redraw
7831         #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
7832             Refresh();
7833         #else
7834             DrawItems(pwc, NULL);
7835         #endif
7836         }
7837     }
7838 
7839     // Clear dont-center-splitter flag if it wasn't set
7840     m_iFlags = (m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER)) | old_flag;
7841 
7842     return res;
7843 }
7844 
7845 // -----------------------------------------------------------------------
7846 
DoHideProperty(wxPGProperty * p,bool hide,int flags)7847 bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags )
7848 {
7849     if ( m_frozen )
7850         return m_pState->DoHideProperty(p, hide, flags);
7851 
7852     wxArrayPGProperty selection = m_pState->m_selection;  // Must use a copy
7853     int selRemoveCount = 0;
7854     for ( unsigned int i=0; i<selection.size(); i++ )
7855     {
7856         wxPGProperty* selected = selection[i];
7857         if ( selected == p || selected->IsSomeParent(p) )
7858         {
7859             if ( !DoRemoveFromSelection(p, flags) )
7860                 return false;
7861             selRemoveCount += 1;
7862         }
7863     }
7864 
7865     m_pState->DoHideProperty(p, hide, flags);
7866 
7867     RecalculateVirtualSize();
7868     Refresh();
7869 
7870     return true;
7871 }
7872 
7873 
7874 // -----------------------------------------------------------------------
7875 // wxPropertyGrid size related methods
7876 // -----------------------------------------------------------------------
7877 
RecalculateVirtualSize(int WXUNUSED (forceXPos))7878 void wxPropertyGrid::RecalculateVirtualSize( int WXUNUSED(forceXPos) )
7879 {
7880     if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE) ||
7881          m_frozen ||
7882          !m_pState )
7883         return;
7884 
7885     //
7886     // If virtual height was changed, then recalculate editor control position(s)
7887     if ( m_pState->m_vhCalcPending )
7888         CorrectEditorWidgetPosY();
7889 
7890     m_pState->EnsureVirtualHeight();
7891 
7892 #ifdef __WXDEBUG__
7893     int by1 = m_pState->GetVirtualHeight();
7894     int by2 = m_pState->GetActualVirtualHeight();
7895     if ( by1 != by2 )
7896     {
7897         wxString s = wxString::Format(wxT("VirtualHeight=%i, ActualVirtualHeight=%i, should match!"), by1, by2);
7898         wxASSERT_MSG( false,
7899                       s.c_str() );
7900         wxLogDebug(s);
7901     }
7902 #endif
7903 
7904     m_iFlags |= wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
7905 
7906     int y = m_pState->m_virtualHeight;
7907 
7908     int width, height;
7909     GetClientSize(&width, &height);
7910 
7911     if ( !HasVirtualWidth() )
7912     {
7913         m_pState->SetVirtualWidth(width);
7914     }
7915 
7916     m_width = width;
7917     m_height = height;
7918 
7919     // Notify users about height problem
7920     wxASSERT_MSG( y <= 32767,
7921                   wxT("Sorry, wxPanel height limitations exceeded") );
7922 
7923     // Set the min size so that the sizer can correctly determine how large
7924     // the virtual window should be.
7925     //
7926     // NB: It is possible that m_canvas is NULL (at least on OS X).
7927     if ( m_canvas )
7928         m_canvas->SetMinSize(wxSize(10, y));
7929 
7930     FitInside();
7931 
7932     m_pState->CheckColumnWidths();
7933 
7934     if ( GetSelection() )
7935         CorrectEditorWidgetSizeX();
7936 
7937     m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
7938 }
7939 
7940 // -----------------------------------------------------------------------
7941 
OnResize(wxSizeEvent & event)7942 void wxPropertyGrid::OnResize( wxSizeEvent& event )
7943 {
7944     if ( !(m_iFlags & wxPG_FL_INITIALIZED) )
7945         return;
7946 
7947     int width, height;
7948     GetClientSize(&width,&height);
7949 
7950     m_width = width;
7951     m_height = height;
7952 
7953     m_visPropArray.SetCount((height/m_lineHeight)+10);
7954 
7955 #if wxPG_DOUBLE_BUFFER
7956     if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
7957     {
7958         int dblh = (m_lineHeight*2);
7959         if ( !m_doubleBuffer )
7960         {
7961             // Create double buffer bitmap to draw on, if none
7962             int w = (width>250)?width:250;
7963             int h = height + dblh;
7964             h = (h>400)?h:400;
7965             m_doubleBuffer = new wxBitmap( w, h );
7966         }
7967         else
7968         {
7969             int w = m_doubleBuffer->GetWidth();
7970             int h = m_doubleBuffer->GetHeight();
7971 
7972             // Double buffer must be large enough
7973             if ( w < width || h < (height+dblh) )
7974             {
7975                 if ( w < width ) w = width;
7976                 if ( h < (height+dblh) ) h = height + dblh;
7977                 delete m_doubleBuffer;
7978                 m_doubleBuffer = new wxBitmap( w, h );
7979             }
7980         }
7981     }
7982 
7983 #endif
7984 
7985     m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true );
7986     m_ncWidth = event.GetSize().x;
7987 
7988     if ( !m_frozen )
7989     {
7990         // This will also call RecalculateVirtualSize()
7991         PrepareAfterItemsAdded();
7992 
7993         Refresh();
7994     }
7995 }
7996 
7997 // -----------------------------------------------------------------------
7998 
SetVirtualWidth(int width)7999 void wxPropertyGrid::SetVirtualWidth( int width )
8000 {
8001     if ( width == -1 )
8002     {
8003         // Disable virtual width
8004         width = GetClientSize().x;
8005         ClearInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH);
8006     }
8007     else
8008     {
8009         // Enable virtual width
8010         SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH);
8011     }
8012     m_pState->SetVirtualWidth( width );
8013 }
8014 
8015 // -----------------------------------------------------------------------
8016 
SetVirtualWidth(int width)8017 void wxPropertyGridState::SetVirtualWidth( int width )
8018 {
8019     // Sometimes width less than 0 is offered. Let's make things easy for
8020     // everybody and deal with it here.
8021     if ( width < 0 )
8022         width = 0;
8023 
8024     wxPropertyGrid* pg = GetGrid();
8025     int gw = pg->GetClientSize().x;
8026     if ( width < gw )
8027         width = gw;
8028 
8029     m_width = width;
8030 }
8031 
8032 // -----------------------------------------------------------------------
8033 
OnClientWidthChange(int newWidth,int widthChange,bool fromOnResize)8034 void wxPropertyGridState::OnClientWidthChange( int newWidth, int widthChange, bool fromOnResize )
8035 {
8036     wxPropertyGrid* pg = GetGrid();
8037 
8038     if ( pg->HasVirtualWidth() )
8039     {
8040         if ( m_width < newWidth )
8041             SetVirtualWidth( newWidth );
8042 
8043         CheckColumnWidths(widthChange);
8044     }
8045     else
8046     {
8047         SetVirtualWidth( newWidth );
8048 
8049         // This should be done before splitter auto centering
8050         // NOTE: Splitter auto-centering is done in this function.
8051         if ( !fromOnResize )
8052             widthChange = 0;
8053         CheckColumnWidths(widthChange);
8054 
8055         if ( !(GetGrid()->GetInternalFlags() & wxPG_FL_SPLITTER_PRE_SET) &&
8056              (GetGrid()->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER) )
8057         {
8058             long timeSinceCreation = (::wxGetLocalTimeMillis() - GetGrid()->m_timeCreated).ToLong();
8059 
8060             // If too long, don't set splitter
8061             if ( timeSinceCreation < 250 )
8062             {
8063                 if ( m_properties->GetCount() )
8064                 {
8065                     SetSplitterLeft( false );
8066                 }
8067                 else
8068                 {
8069                     DoSetSplitterPosition( newWidth / 2 );
8070                     GetGrid()->ClearInternalFlag(wxPG_FL_SPLITTER_PRE_SET);
8071                 }
8072             }
8073         }
8074     }
8075 }
8076 
8077 // -----------------------------------------------------------------------
8078 // wxPropertyGrid mouse event handling
8079 // -----------------------------------------------------------------------
8080 
8081 // selFlags uses same values DoSelectProperty's flags
8082 // Returns true if event was vetoed.
SendEvent(int eventType,wxPGProperty * p,wxVariant * pValue,unsigned int selFlags,unsigned int column)8083 bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p,
8084                                 wxVariant* pValue,
8085                                 unsigned int selFlags,
8086                                 unsigned int column )
8087 {
8088     // Send property grid event of specific type and with specific property
8089     wxPropertyGridEvent evt( eventType, m_eventObject->GetId() );
8090     evt.SetPropertyGrid(this);
8091     evt.SetEventObject(m_eventObject);
8092     evt.SetProperty(p);
8093     evt.SetColumn(column);
8094     if ( pValue )
8095     {
8096         evt.SetCanVeto(true);
8097         evt.SetupValidationInfo();
8098         m_validationInfo.m_pValue = pValue;
8099     }
8100     else if ( !(selFlags & wxPG_SEL_NOVALIDATE) )
8101     {
8102         evt.SetCanVeto(true);
8103     }
8104 
8105     wxEvtHandler* evtHandler = m_eventObject->GetEventHandler();
8106 
8107     wxPropertyGridEvent* prevProcessedEvent = m_processedEvent;
8108     m_processedEvent = &evt;
8109 
8110     evtHandler->ProcessEvent(evt);
8111 
8112     m_processedEvent = prevProcessedEvent;
8113 
8114     return evt.WasVetoed();
8115 }
8116 
8117 // -----------------------------------------------------------------------
8118 
8119 // Return false if should be skipped
HandleMouseClick(int x,unsigned int y,wxMouseEvent & event)8120 bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &event )
8121 {
8122     bool res = true;
8123 
8124     // Need to set focus?
8125     if ( !(m_iFlags & wxPG_FL_FOCUSED) )
8126     {
8127         SetFocusOnCanvas();
8128     }
8129 
8130     wxPropertyGridState* state = m_pState;
8131     int splitterHit;
8132     int splitterHitOffset;
8133     int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
8134 
8135     wxPGProperty* p = DoGetItemAtY(y);
8136 
8137     if ( p )
8138     {
8139         int depth = (int)p->GetDepth() - 1;
8140 
8141         int marginEnds = m_marginWidth + ( depth * m_subgroup_extramargin );
8142 
8143         if ( x >= marginEnds )
8144         {
8145             // Outside margin.
8146 
8147             if ( p->IsCategory() )
8148             {
8149                 // Click on a category.caption
8150                 wxPropertyCategory* pwc = (wxPropertyCategory*)p;
8151 
8152                 int textX = m_marginWidth + ((unsigned int)((pwc->m_depth-1)*m_subgroup_extramargin));
8153 
8154                 // Expand, collapse, activate etc. if click on text or left of splitter.
8155                 if ( x >= textX
8156                      &&
8157                      ( x < (textX+pwc->GetTextExtent(this, m_captionFont)+(wxPG_CAPRECTXMARGIN*2)) ||
8158                        columnHit == 0
8159                      )
8160                     )
8161                 {
8162                     if ( !AddToSelectionFromInputEvent( p, columnHit,
8163                                                         &event ) )
8164                         return res;
8165 
8166                     // On double-click, expand/collapse.
8167                     if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
8168                     {
8169                         if ( pwc->IsExpanded() ) DoCollapse( p, true );
8170                         else DoExpand( p, true );
8171                     }
8172                 }
8173             }
8174             else if ( splitterHit == -1 )
8175             {
8176                 // Click on cell
8177                 unsigned int selFlag = 0;
8178                 if ( columnHit == 1 )
8179                 {
8180                     m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK;
8181                     selFlag = wxPG_SEL_FOCUS;
8182                 }
8183                 if ( !AddToSelectionFromInputEvent( p, columnHit,
8184                                                     &event, selFlag ) )
8185                     return res;
8186 
8187                 m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK);
8188 
8189                 if ( p->GetChildCount() && !p->IsCategory() )
8190                     // On double-click, expand/collapse.
8191                     if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
8192                     {
8193                         wxPGProperty* pwc = (wxPGProperty*)p;
8194                         if ( pwc->IsExpanded() ) DoCollapse( p, true );
8195                         else DoExpand( p, true );
8196                     }
8197 
8198                 res = false;
8199             }
8200             else
8201             {
8202                 // Click on splitter
8203                 if ( !(m_windowStyle & wxPG_STATIC_SPLITTER) )
8204                 {
8205                     if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
8206                     {
8207                         // Double-clicking the splitter causes auto-centering
8208                         ResetColumnSizes( true );
8209                     }
8210                     else if ( m_dragStatus == 0 )
8211                     {
8212                         //
8213                         // Begin draggin the splitter
8214                         //
8215 
8216                         // send event
8217                         DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
8218 
8219                         if ( m_wndEditor )
8220                         {
8221                             // Changes must be committed here or the
8222                             // value won't be drawn correctly
8223                             if ( !CommitChangesFromEditor() )
8224                                 return res;
8225 
8226                             m_wndEditor->Show ( false );
8227                         }
8228 
8229                         if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
8230                         {
8231                             m_canvas->CaptureMouse();
8232                             m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
8233                         }
8234 
8235                         m_dragStatus = 1;
8236                         m_draggedSplitter = splitterHit;
8237                         m_dragOffset = splitterHitOffset;
8238 
8239                         wxClientDC dc(m_canvas);
8240 
8241                     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
8242                         // Fixes button disappearance bug
8243                         if ( m_wndEditor2 )
8244                             m_wndEditor2->Show ( false );
8245                     #endif
8246 
8247                         m_startingSplitterX = x - splitterHitOffset;
8248                     }
8249                 }
8250             }
8251         }
8252         else
8253         {
8254             // Click on margin
8255             if ( p->GetChildCount() )
8256             {
8257                 int nx = x + m_marginWidth - marginEnds; // Normalize x.
8258 
8259                 //
8260                 // Fine tune cell button x
8261                 if ( !p->IsCategory() )
8262                     nx -= IN_CELL_EXPANDER_BUTTON_X_ADJUST;
8263 
8264                 if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
8265                 {
8266                     int y2 = y % m_lineHeight;
8267                     if ( (y2 >= m_buttonSpacingY && y2 < (m_buttonSpacingY+m_iconHeight)) )
8268                     {
8269                         // On click on expander button, expand/collapse
8270                         if ( ((wxPGProperty*)p)->IsExpanded() )
8271                             DoCollapse( p, true );
8272                         else
8273                             DoExpand( p, true );
8274                     }
8275                 }
8276             }
8277         }
8278     }
8279     return res;
8280 }
8281 
8282 // -----------------------------------------------------------------------
8283 
HandleMouseRightClick(int WXUNUSED (x),unsigned int WXUNUSED (y),wxMouseEvent & event)8284 bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), unsigned int WXUNUSED(y),
8285                                             wxMouseEvent& event )
8286 {
8287     if ( m_propHover )
8288     {
8289         // Select property here as well
8290         wxPGProperty* p = m_propHover;
8291         AddToSelectionFromInputEvent(p, m_colHover, &event);
8292 
8293         // Send right click event.
8294         SendEvent( wxEVT_PG_RIGHT_CLICK, p );
8295 
8296         return true;
8297     }
8298     return false;
8299 }
8300 
8301 // -----------------------------------------------------------------------
8302 
HandleMouseDoubleClick(int WXUNUSED (x),unsigned int WXUNUSED (y),wxMouseEvent & event)8303 bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), unsigned int WXUNUSED(y),
8304                                              wxMouseEvent& event )
8305 {
8306     if ( m_propHover )
8307     {
8308         // Select property here as well
8309         wxPGProperty* p = m_propHover;
8310 
8311         AddToSelectionFromInputEvent(p, m_colHover, &event);
8312 
8313         // Send double-click event.
8314         SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover );
8315 
8316         return true;
8317     }
8318     return false;
8319 }
8320 
8321 // -----------------------------------------------------------------------
8322 
8323 #if wxPG_SUPPORT_TOOLTIPS
8324 
SetToolTip(const wxString & tipString)8325 void wxPropertyGrid::SetToolTip( const wxString& tipString )
8326 {
8327     if ( tipString.length() )
8328     {
8329         m_canvas->SetToolTip(tipString);
8330     }
8331     else
8332     {
8333     #if wxPG_ALLOW_EMPTY_TOOLTIPS
8334         m_canvas->SetToolTip( m_emptyString );
8335     #else
8336         m_canvas->SetToolTip( NULL );
8337     #endif
8338     }
8339 }
8340 
8341 #endif // #if wxPG_SUPPORT_TOOLTIPS
8342 
8343 // -----------------------------------------------------------------------
8344 
8345 // Return false if should be skipped
HandleMouseMove(int x,unsigned int y,wxMouseEvent & event)8346 bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event )
8347 {
8348     // Safety check (needed because mouse capturing may
8349     // otherwise freeze the control)
8350     if ( m_dragStatus > 0 && !event.Dragging() )
8351     {
8352         HandleMouseUp(x,y,event);
8353     }
8354 
8355     wxPropertyGridState* state = m_pState;
8356     int splitterHit;
8357     int splitterHitOffset;
8358     int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
8359     int splitterX = x - splitterHitOffset;
8360 
8361     m_colHover = columnHit;
8362 
8363     if ( m_dragStatus > 0 )
8364     {
8365         if ( x > (m_marginWidth + wxPG_DRAG_MARGIN) &&
8366              x < (m_pState->m_width - wxPG_DRAG_MARGIN) )
8367         {
8368 
8369             int newSplitterX = x - m_dragOffset;
8370 
8371             // Splitter redraw required?
8372             if ( newSplitterX != splitterX )
8373             {
8374                 // Move everything
8375                 SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
8376                 state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false );
8377                 state->m_fSplitterX = (float) newSplitterX;
8378 
8379                 if ( GetSelection() )
8380                     CorrectEditorWidgetSizeX();
8381 
8382                 Update();
8383                 Refresh();
8384             }
8385 
8386             m_dragStatus = 2;
8387         }
8388 
8389         return false;
8390     }
8391     else
8392     {
8393 
8394         int ih = m_lineHeight;
8395         int sy = y;
8396 
8397     #if wxPG_SUPPORT_TOOLTIPS
8398         wxPGProperty* prevHover = m_propHover;
8399         unsigned char prevSide = m_mouseSide;
8400     #endif
8401         int curPropHoverY = y - (y % ih);
8402 
8403         // On which item it hovers
8404         if ( ( !m_propHover )
8405              ||
8406              ( m_propHover && ( sy < m_propHoverY || sy >= (m_propHoverY+ih) ) )
8407            )
8408         {
8409             // Mouse moves on another property
8410 
8411             m_propHover = DoGetItemAtY(y);
8412             m_propHoverY = curPropHoverY;
8413 
8414             // Send hover event
8415             SendEvent( wxEVT_PG_HIGHLIGHTED, m_propHover );
8416         }
8417 
8418     #if wxPG_SUPPORT_TOOLTIPS
8419         // Store which side we are on
8420         m_mouseSide = 0;
8421         if ( columnHit == 1 )
8422             m_mouseSide = 2;
8423         else if ( columnHit == 0 )
8424             m_mouseSide = 1;
8425 
8426         //
8427         // If tooltips are enabled, show label or value as a tip
8428         // in case it doesn't otherwise show in full length.
8429         //
8430         if ( m_windowStyle & wxPG_TOOLTIPS )
8431         {
8432             wxToolTip* tooltip = m_canvas->GetToolTip();
8433 
8434             if ( m_propHover != prevHover || prevSide != m_mouseSide )
8435             {
8436                 if ( m_propHover && !m_propHover->IsCategory() )
8437                 {
8438 
8439                     if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS )
8440                     {
8441                         // Show help string as a tooltip
8442                         wxString tipString = m_propHover->GetHelpString();
8443 
8444                         SetToolTip(tipString);
8445                     }
8446                     else
8447                     {
8448                         // Show cropped value string as a tooltip
8449                         wxString tipString;
8450                         int space = 0;
8451 
8452                         if ( m_mouseSide == 1 )
8453                         {
8454                             tipString = m_propHover->m_label;
8455                             space = splitterX-m_marginWidth-3;
8456                         }
8457                         else if ( m_mouseSide == 2 )
8458                         {
8459                             tipString = m_propHover->GetDisplayedString();
8460 
8461                             space = m_width - splitterX;
8462                             if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
8463                                 space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
8464                         }
8465 
8466                         if ( space )
8467                         {
8468                             int tw, th;
8469     	                    GetTextExtent( tipString, &tw, &th, 0, 0 );
8470                             if ( tw > space )
8471                             {
8472                                 SetToolTip( tipString );
8473                             }
8474                         }
8475                         else
8476                         {
8477                             if ( tooltip )
8478                             {
8479                             #if wxPG_ALLOW_EMPTY_TOOLTIPS
8480                                 m_canvas->SetToolTip( m_emptyString );
8481                             #else
8482                                 m_canvas->SetToolTip( NULL );
8483                             #endif
8484                             }
8485                         }
8486 
8487                     }
8488                 }
8489                 else
8490                 {
8491                     if ( tooltip )
8492                     {
8493                     #if wxPG_ALLOW_EMPTY_TOOLTIPS
8494                         m_canvas->SetToolTip( m_emptyString );
8495                     #else
8496                         m_canvas->SetToolTip( NULL );
8497                     #endif
8498                     }
8499                 }
8500             }
8501         }
8502     #endif
8503 
8504         if ( splitterHit == -1 ||
8505              !m_propHover ||
8506              HasFlag(wxPG_STATIC_SPLITTER) )
8507         {
8508             // hovering on something else
8509             if ( m_curcursor != wxCURSOR_ARROW )
8510                 CustomSetCursor( wxCURSOR_ARROW );
8511         }
8512         else
8513         {
8514             // Do not allow splitter cursor on caption items.
8515             // (also not if we were dragging and its started
8516             // outside the splitter region)
8517 
8518             if ( m_propHover &&
8519                  !m_propHover->IsCategory() &&
8520                  !event.Dragging() )
8521             {
8522 
8523                 // hovering on splitter
8524 
8525                 // NB: Condition disabled since MouseLeave event (from the editor control) cannot be
8526                 //     reliably detected.
8527                 //if ( m_curcursor != wxCURSOR_SIZEWE )
8528                 CustomSetCursor( wxCURSOR_SIZEWE, true );
8529 
8530                 return false;
8531             }
8532             else
8533             {
8534                 // hovering on something else
8535                 if ( m_curcursor != wxCURSOR_ARROW )
8536                     CustomSetCursor( wxCURSOR_ARROW );
8537             }
8538         }
8539 
8540         //
8541         // Multi select by dragging
8542         //
8543         if ( (GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) &&
8544              event.LeftIsDown() &&
8545              m_propHover &&
8546              GetSelection() &&
8547              columnHit != 1 &&
8548              !state->DoIsPropertySelected(m_propHover) )
8549         {
8550             // Additional requirement is that the hovered property
8551             // is adjacent to edges of selection.
8552             const wxArrayPGProperty& selection = GetSelectedProperties();
8553 
8554             // Since categories cannot be selected along with 'other'
8555             // properties, exclude them from iterator flags.
8556             int iterFlags = wxPG_ITERATE_VISIBLE & (~wxPG_PROP_CATEGORY);
8557 
8558             for ( int i=(selection.size()-1); i>=0; i-- )
8559             {
8560                 // TODO: This could be optimized by keeping track of
8561                 //       which properties are at the edges of selection.
8562                 wxPGProperty* selProp = selection[i];
8563                 if ( state->ArePropertiesAdjacent(m_propHover, selProp,
8564                                                   iterFlags) )
8565                 {
8566                     DoAddToSelection(m_propHover);
8567                     break;
8568                 }
8569             }
8570         }
8571     }
8572     return true;
8573 }
8574 
8575 // -----------------------------------------------------------------------
8576 
8577 // Also handles Leaving event
HandleMouseUp(int x,unsigned int WXUNUSED (y),wxMouseEvent & WXUNUSED (event))8578 bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y),
8579                                     wxMouseEvent &WXUNUSED(event) )
8580 {
8581     wxPropertyGridState* state = m_pState;
8582     bool res = false;
8583 
8584     int splitterHit;
8585     int splitterHitOffset;
8586     state->HitTestH( x, &splitterHit, &splitterHitOffset );
8587 
8588     // No event type check - basicly calling this method should
8589     // just stop dragging.
8590     // Left up after dragged?
8591     if ( m_dragStatus >= 1 )
8592     {
8593     //
8594     // End Splitter Dragging
8595     //
8596         // DO NOT ENABLE FOLLOWING LINE!
8597         // (it is only here as a reminder to not to do it)
8598         //splitterX = x;
8599 
8600         // Disable splitter auto-centering
8601         m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
8602 
8603         // This is necessary to return cursor
8604         if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
8605         {
8606             m_canvas->ReleaseMouse();
8607             m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
8608         }
8609 
8610         // Set back the default cursor, if necessary
8611         if ( splitterHit == -1 ||
8612              !m_propHover )
8613         {
8614             CustomSetCursor( wxCURSOR_ARROW );
8615         }
8616 
8617         m_dragStatus = 0;
8618 
8619         // Control background needs to be cleared
8620         wxPGProperty* selected = GetSelection();
8621         if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && selected )
8622             DrawItem( selected );
8623 
8624         if ( m_wndEditor )
8625         {
8626             m_wndEditor->Show( true );
8627         }
8628 
8629     #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
8630         // Fixes button disappearance bug
8631         if ( m_wndEditor2 )
8632             m_wndEditor2->Show( true );
8633     #endif
8634 
8635         // This clears the focus.
8636         m_editorFocused = 0;
8637 
8638     }
8639     return res;
8640 }
8641 
8642 // -----------------------------------------------------------------------
8643 
OnMouseCommon(wxMouseEvent & event,int * px,int * py)8644 bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py )
8645 {
8646     int splitterX = GetSplitterPosition();
8647 
8648     //int ux, uy;
8649     //CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
8650     int ux = event.m_x;
8651     int uy = event.m_y;
8652 
8653     wxWindow* wnd = GetEditorControl();
8654 
8655     // Hide popup on clicks
8656     if ( event.GetEventType() != wxEVT_MOTION )
8657         if ( wnd && wnd->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
8658         {
8659             ((wxPGOwnerDrawnComboBox*)wnd)->HidePopup();
8660         }
8661 
8662     wxRect r;
8663     if ( wnd )
8664         r = wnd->GetRect();
8665     if ( wnd == (wxWindow*) NULL || m_dragStatus ||
8666          (
8667            ux <= (splitterX + wxPG_SPLITTERX_DETECTMARGIN2) ||
8668            ux >= (r.x+r.width) ||
8669            event.m_y < r.y ||
8670            event.m_y >= (r.y+r.height)
8671          )
8672        )
8673     {
8674         *px = ux;
8675         *py = uy;
8676         return true;
8677     }
8678     else
8679     {
8680         if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
8681     }
8682     return false;
8683 }
8684 
8685 // -----------------------------------------------------------------------
8686 
OnMouseClick(wxMouseEvent & event)8687 void wxPropertyGrid::OnMouseClick( wxMouseEvent &event )
8688 {
8689     int x, y;
8690     if ( OnMouseCommon( event, &x, &y ) )
8691     {
8692         HandleMouseClick(x,y,event);
8693     }
8694 }
8695 
8696 // -----------------------------------------------------------------------
8697 
OnMouseRightClick(wxMouseEvent & event)8698 void wxPropertyGrid::OnMouseRightClick( wxMouseEvent &event )
8699 {
8700     int x, y;
8701     CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
8702     HandleMouseRightClick(x,y,event);
8703 }
8704 
8705 // -----------------------------------------------------------------------
8706 
OnMouseDoubleClick(wxMouseEvent & event)8707 void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event )
8708 {
8709     // Always run standard mouse-down handler as well
8710     OnMouseClick(event);
8711 
8712     int x, y;
8713     CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
8714     HandleMouseDoubleClick(x,y,event);
8715 }
8716 
8717 // -----------------------------------------------------------------------
8718 
OnMouseMove(wxMouseEvent & event)8719 void wxPropertyGrid::OnMouseMove( wxMouseEvent &event )
8720 {
8721     int x, y;
8722     if ( OnMouseCommon( event, &x, &y ) )
8723     {
8724         HandleMouseMove(x,y,event);
8725     }
8726 }
8727 
8728 // -----------------------------------------------------------------------
8729 
OnMouseMoveBottom(wxMouseEvent & WXUNUSED (event))8730 void wxPropertyGrid::OnMouseMoveBottom( wxMouseEvent& WXUNUSED(event) )
8731 {
8732     // Called when mouse moves in the empty space below the properties.
8733     CustomSetCursor( wxCURSOR_ARROW );
8734 }
8735 
8736 // -----------------------------------------------------------------------
8737 
OnMouseUp(wxMouseEvent & event)8738 void wxPropertyGrid::OnMouseUp( wxMouseEvent &event )
8739 {
8740     int x, y;
8741     if ( OnMouseCommon( event, &x, &y ) )
8742     {
8743         HandleMouseUp(x,y,event);
8744     }
8745 }
8746 
8747 // -----------------------------------------------------------------------
8748 
OnMouseEntry(wxMouseEvent & event)8749 void wxPropertyGrid::OnMouseEntry( wxMouseEvent &event )
8750 {
8751     // This may get called from child control as well, so event's
8752     // mouse position cannot be relied on.
8753 
8754     if ( event.Entering() )
8755     {
8756         if ( !(m_iFlags & wxPG_FL_MOUSE_INSIDE) )
8757         {
8758             // TODO: Fix this (detect parent and only do
8759             //   cursor trick if it is a manager).
8760             wxASSERT( GetParent() );
8761             GetParent()->SetCursor(wxNullCursor);
8762 
8763             m_iFlags |= wxPG_FL_MOUSE_INSIDE;
8764         }
8765         else
8766             GetParent()->SetCursor(wxNullCursor);
8767     }
8768     else if ( event.Leaving() )
8769     {
8770         // Without this, wxSpinCtrl editor will sometimes have wrong cursor
8771         m_canvas->SetCursor( wxNullCursor );
8772 
8773         // Get real cursor position
8774         wxPoint pt = ScreenToClient(::wxGetMousePosition());
8775 
8776         if ( ( pt.x <= 0 || pt.y <= 0 || pt.x >= m_width || pt.y >= m_height ) )
8777         {
8778             {
8779                 if ( (m_iFlags & wxPG_FL_MOUSE_INSIDE) )
8780                 {
8781                     m_iFlags &= ~(wxPG_FL_MOUSE_INSIDE);
8782                 }
8783 
8784                 if ( m_dragStatus )
8785                     wxPropertyGrid::HandleMouseUp ( -1, 10000, event );
8786             }
8787         }
8788     }
8789 
8790     event.Skip();
8791 }
8792 
8793 // -----------------------------------------------------------------------
8794 
8795 // Common code used by various OnMouseXXXChild methods.
OnMouseChildCommon(wxMouseEvent & event,int * px,int * py)8796 bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent &event, int* px, int *py )
8797 {
8798     wxWindow* topCtrlWnd = (wxWindow*)event.GetEventObject();
8799     wxASSERT( topCtrlWnd );
8800     int x, y;
8801     event.GetPosition(&x,&y);
8802 
8803     AdjustPosForClipperWindow( topCtrlWnd, &x, &y );
8804 
8805     int splitterX = GetSplitterPosition();
8806 
8807     wxRect r = topCtrlWnd->GetRect();
8808     if ( !m_dragStatus &&
8809          x > (splitterX-r.x+wxPG_SPLITTERX_DETECTMARGIN2) &&
8810          y >= 0 && y < r.height \
8811        )
8812     {
8813         if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
8814         event.Skip();
8815     }
8816     else
8817     {
8818         CalcUnscrolledPosition( event.m_x + r.x, event.m_y + r.y, \
8819             px, py );
8820         return true;
8821     }
8822     return false;
8823 }
8824 
OnMouseClickChild(wxMouseEvent & event)8825 void wxPropertyGrid::OnMouseClickChild( wxMouseEvent &event )
8826 {
8827     int x,y;
8828     if ( OnMouseChildCommon(event,&x,&y) )
8829     {
8830         bool res = HandleMouseClick(x,y,event);
8831         if ( !res ) event.Skip();
8832     }
8833 }
8834 
OnMouseRightClickChild(wxMouseEvent & event)8835 void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent &event )
8836 {
8837     int x,y;
8838     wxASSERT( m_wndEditor );
8839     // These coords may not be exact (about +-2),
8840     // but that should not matter (right click is about item, not position).
8841     wxPoint pt = m_wndEditor->GetPosition();
8842     CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y );
8843 
8844     // FIXME: Used to set m_propHover to selection here. Was it really
8845     //        necessary?
8846 
8847     bool res = HandleMouseRightClick(x,y,event);
8848     if ( !res ) event.Skip();
8849 }
8850 
OnMouseMoveChild(wxMouseEvent & event)8851 void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent &event )
8852 {
8853     int x,y;
8854     if ( OnMouseChildCommon(event,&x,&y) )
8855     {
8856         bool res = HandleMouseMove(x,y,event);
8857         if ( !res ) event.Skip();
8858     }
8859 }
8860 
OnMouseUpChild(wxMouseEvent & event)8861 void wxPropertyGrid::OnMouseUpChild( wxMouseEvent &event )
8862 {
8863     int x,y;
8864     if ( OnMouseChildCommon(event,&x,&y) )
8865     {
8866         bool res = HandleMouseUp(x,y,event);
8867         if ( !res ) event.Skip();
8868     }
8869 }
8870 
8871 // -----------------------------------------------------------------------
8872 // wxPropertyGrid keyboard event handling
8873 // -----------------------------------------------------------------------
8874 
SendNavigationKeyEvent(int dir)8875 void wxPropertyGrid::SendNavigationKeyEvent( int dir )
8876 {
8877     wxNavigationKeyEvent evt;
8878     evt.SetFlags(wxNavigationKeyEvent::FromTab|
8879                  (dir?wxNavigationKeyEvent::IsForward:
8880                       wxNavigationKeyEvent::IsBackward));
8881     evt.SetEventObject(this);
8882     m_canvas->GetEventHandler()->AddPendingEvent(evt);
8883 }
8884 
8885 
KeyEventToActions(wxKeyEvent & event,int * pSecond) const8886 int wxPropertyGrid::KeyEventToActions(wxKeyEvent &event, int* pSecond) const
8887 {
8888     // Translates wxKeyEvent to wxPG_ACTION_XXX
8889 
8890     int keycode = event.GetKeyCode();
8891 
8892     int modifiers = event.GetModifiers();
8893 
8894     wxASSERT( !(modifiers&~(0xFFFF)) );
8895 
8896     int hashMapKey = (keycode & 0xFFFF) | ((modifiers & 0xFFFF) << 16);
8897 
8898     wxPGHashMapI2I::const_iterator it = m_actionTriggers.find(hashMapKey);
8899 
8900     if ( it == m_actionTriggers.end() )
8901         return 0;
8902 
8903     if ( pSecond )
8904     {
8905         int second = (it->second>>16) & 0xFFFF;
8906         *pSecond = second;
8907     }
8908 
8909     return (it->second & 0xFFFF);
8910 }
8911 
AddActionTrigger(int action,int keycode,int modifiers)8912 void wxPropertyGrid::AddActionTrigger( int action, int keycode, int modifiers )
8913 {
8914     wxASSERT( !(modifiers&~(0xFFFF)) );
8915 
8916     int hashMapKey = (keycode & 0xFFFF) | ((modifiers & 0xFFFF) << 16);
8917 
8918     wxPGHashMapI2I::iterator it = m_actionTriggers.find(hashMapKey);
8919 
8920     if ( it != m_actionTriggers.end() )
8921     {
8922         // This key combination is already used
8923 
8924         // Can add secondary?
8925         wxASSERT_MSG( !(it->second&~(0xFFFF)),
8926                       wxT("You can only add up to two separate actions per key combination.") );
8927 
8928         action = it->second | (action<<16);
8929     }
8930 
8931     m_actionTriggers[hashMapKey] = action;
8932 }
8933 
ClearActionTriggers(int action)8934 void wxPropertyGrid::ClearActionTriggers( int action )
8935 {
8936     wxPGHashMapI2I::iterator it;
8937     bool didSomething;
8938 
8939     do
8940     {
8941         didSomething = false;
8942 
8943         for ( it = m_actionTriggers.begin();
8944               it != m_actionTriggers.end();
8945               it++ )
8946         {
8947             if ( it->second == action )
8948             {
8949                 m_actionTriggers.erase(it);
8950                 didSomething = true;
8951                 break;
8952             }
8953         }
8954     }
8955     while ( didSomething );
8956 }
8957 
CopyTextToClipboard(const wxString & text)8958 static void CopyTextToClipboard( const wxString& text )
8959 {
8960     if ( wxTheClipboard->Open() )
8961     {
8962         // This data objects are held by the clipboard,
8963         // so do not delete them in the app.
8964         wxTheClipboard->SetData( new wxTextDataObject(text) );
8965         wxTheClipboard->Close();
8966     }
8967 }
8968 
HandleKeyEvent(wxKeyEvent & event)8969 void wxPropertyGrid::HandleKeyEvent(wxKeyEvent &event)
8970 {
8971     //
8972     // Handles key event when editor control is not focused.
8973     //
8974 
8975     wxASSERT( !m_frozen );
8976     if ( m_frozen )
8977         return;
8978 
8979     // Travelsal between items, collapsing/expanding, etc.
8980     int keycode = event.GetKeyCode();
8981 
8982     if ( keycode == WXK_TAB )
8983     {
8984         if ( HasFlag(wxTAB_TRAVERSAL) )
8985             SendNavigationKeyEvent( event.ShiftDown()?0:1 );
8986         else
8987             event.Skip();
8988 
8989         return;
8990     }
8991 
8992     // Ignore Alt and Control when they are down alone
8993     if ( keycode == WXK_ALT ||
8994          keycode == WXK_CONTROL )
8995     {
8996         event.Skip();
8997         return;
8998     }
8999 
9000     int secondAction;
9001     int action = KeyEventToActions(event, &secondAction);
9002 
9003     wxPGProperty* selected = GetSelection();
9004 
9005     if ( selected )
9006     {
9007         // Show dialog?
9008         if ( ButtonTriggerKeyTest(event) )
9009             return;
9010 
9011         wxPGProperty* p = selected;
9012 
9013         if ( action == wxPG_ACTION_EDIT )
9014         {
9015             DoSelectAndEdit(p, 1, wxPG_SEL_FOCUS);
9016         }
9017         else if ( action == wxPG_ACTION_COPY )
9018         {
9019             CopyTextToClipboard(p->GetDisplayedString());
9020         }
9021         else
9022         {
9023             // Travel and expand/collapse
9024             int selectDir = -2;
9025 
9026             if ( p->GetChildCount() )
9027             {
9028                 if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY )
9029                 {
9030                     if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse(p) )
9031                         keycode = 0;
9032                 }
9033                 else if ( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY )
9034                 {
9035                     if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand(p) )
9036                         keycode = 0;
9037                 }
9038             }
9039 
9040             if ( keycode )
9041             {
9042                 if ( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY )
9043                 {
9044                     selectDir = -1;
9045                 }
9046                 else if ( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY )
9047                 {
9048                     selectDir = 1;
9049                 }
9050                 else
9051                 {
9052                     event.Skip();
9053                 }
9054 
9055             }
9056 
9057             if ( selectDir >= -1 )
9058             {
9059                 p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir );
9060                 if ( p )
9061                     DoSelectProperty(p);
9062             }
9063         }
9064     }
9065     else
9066     {
9067         // If nothing was selected, select the first item now
9068         // (or navigate out of tab).
9069         if ( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT )
9070         {
9071             wxPGProperty* p = wxPropertyGridInterface::GetFirst();
9072             if ( p ) DoSelectProperty(p);
9073         }
9074     }
9075 }
9076 
9077 // -----------------------------------------------------------------------
9078 
9079 // Potentially handles a keyboard event for editor controls.
9080 // Returns false if event should *not* be skipped (on true it can
9081 // be optionally skipped).
9082 // Basicly, false means that SelectProperty was called (or was about
9083 // to be called, if canDestroy was false).
HandleChildKey(wxKeyEvent & event)9084 bool wxPropertyGrid::HandleChildKey( wxKeyEvent& event )
9085 {
9086     bool res = true;
9087 
9088     wxPGProperty* selected = GetSelection();
9089 
9090     if ( !selected || !m_wndEditor )
9091     {
9092         return true;
9093     }
9094 
9095     int action = KeyEventToAction(event);
9096 
9097     // Unfocus?
9098     if ( action == wxPG_ACTION_CANCEL_EDIT )
9099     {
9100         //
9101         // Esc cancels any changes
9102         if ( IsEditorsValueModified() )
9103         {
9104             EditorsValueWasNotModified();
9105 
9106             // Update the control as well
9107             selected->GetEditorClass()->SetControlStringValue( selected,
9108                 GetEditorControl(),
9109                 selected->GetDisplayedString() );
9110         }
9111 
9112         OnValidationFailureReset(selected);
9113 
9114         res = false;
9115 
9116         UnfocusEditor();
9117     }
9118     else if ( action == wxPG_ACTION_COPY )
9119     {
9120         // NB: There is some problem with getting native cut-copy-paste keys to go through
9121         //     for embedded editor wxTextCtrl. This is why we emulate.
9122         //
9123         wxTextCtrl* tc = GetEditorTextCtrl();
9124         if ( tc )
9125         {
9126             wxString sel = tc->GetStringSelection();
9127             if ( sel.length() )
9128                 CopyTextToClipboard(sel);
9129         }
9130         else
9131         {
9132             CopyTextToClipboard(selected->GetDisplayedString());
9133         }
9134     }
9135     else if ( action == wxPG_ACTION_CUT )
9136     {
9137         wxTextCtrl* tc = GetEditorTextCtrl();
9138         if ( tc )
9139         {
9140             long from, to;
9141             tc->GetSelection(&from, &to);
9142             if ( from < to )
9143             {
9144                 CopyTextToClipboard(tc->GetStringSelection());
9145                 if ( !tc->HasFlag(wxTE_READONLY) )
9146                     tc->Remove(from, to);
9147             }
9148         }
9149     }
9150     else if ( action == wxPG_ACTION_PASTE )
9151     {
9152         wxTextCtrl* tc = GetEditorTextCtrl();
9153         if ( tc && !tc->HasFlag(wxTE_READONLY) )
9154         {
9155             if (wxTheClipboard->Open())
9156             {
9157                 if (wxTheClipboard->IsSupported( wxDF_TEXT ))
9158                 {
9159                     wxTextDataObject data;
9160                     wxTheClipboard->GetData( data );
9161                     long from, to;
9162                     tc->GetSelection(&from, &to);
9163                     if ( from < to )
9164                     {
9165                         tc->Remove(from, to);
9166                         tc->WriteText(data.GetText());
9167                     }
9168                     else
9169                     {
9170                         tc->WriteText(data.GetText());
9171                     }
9172                 }
9173                 wxTheClipboard->Close();
9174             }
9175         }
9176     }
9177     else if ( action == wxPG_ACTION_SELECT_ALL )
9178     {
9179         wxTextCtrl* tc = GetEditorTextCtrl();
9180         if ( tc )
9181             tc->SetSelection(-1, -1);
9182     }
9183 
9184     int keycode = event.GetKeyCode();
9185 
9186     //
9187     // Do not ever skip page up or page down. Those events would propagate
9188     // into the wxScrolledWindow and may cause unwanted scrolling (at least
9189     // under wxGTK).
9190     if ( keycode == WXK_PAGEUP || keycode == WXK_PAGEDOWN )
9191         res = false;
9192 
9193     return res;
9194 }
9195 
9196 // -----------------------------------------------------------------------
9197 
OnKey(wxKeyEvent & event)9198 void wxPropertyGrid::OnKey( wxKeyEvent &event )
9199 {
9200 
9201     //
9202     // Events to editor controls should get relayed here.
9203     //
9204     wxWindow* focused = wxWindow::FindFocus();
9205 
9206     wxWindow* primaryCtrl = GetEditorControl();
9207 
9208     if ( primaryCtrl &&
9209          (focused==primaryCtrl
9210           || m_editorFocused) )
9211     {
9212         // Child key must be processed here, since it can
9213         // destroy the control which is referred by its own
9214         // event handling.
9215         HandleChildKey( event );
9216     }
9217     else
9218         HandleKeyEvent( event );
9219 }
9220 
9221 // -----------------------------------------------------------------------
9222 
OnKeyUp(wxKeyEvent & event)9223 void wxPropertyGrid::OnKeyUp(wxKeyEvent &event)
9224 {
9225     m_keyComboConsumed = 0;
9226 
9227     event.Skip();
9228 }
9229 
9230 // -----------------------------------------------------------------------
9231 
OnNavigationKey(wxNavigationKeyEvent & event)9232 void wxPropertyGrid::OnNavigationKey( wxNavigationKeyEvent& event )
9233 {
9234     // Ignore events that occur very close to focus set
9235     if ( m_iFlags & wxPG_FL_IGNORE_NEXT_NAVKEY )
9236     {
9237         m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
9238         event.Skip();
9239         return;
9240     }
9241 
9242     wxPGProperty* next = (wxPGProperty*) NULL;
9243     wxPGProperty* selected = GetSelection();
9244 
9245     int dir = event.GetDirection()?1:-1;
9246 
9247     if ( selected )
9248     {
9249         if ( dir == 1 && (m_wndEditor || m_wndEditor2) )
9250         {
9251             wxWindow* focused = wxWindow::FindFocus();
9252 
9253             wxWindow* wndToCheck = GetEditorControl();
9254 
9255             // ODComboBox focus goes to its text ctrl, so we need to use it instead
9256             if ( wndToCheck && wndToCheck->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
9257             {
9258                 wxTextCtrl* comboTextCtrl = ((wxPGOwnerDrawnComboBox*)wndToCheck)->GetTextCtrl();
9259                 if ( comboTextCtrl )
9260                     wndToCheck = comboTextCtrl;
9261             }
9262 
9263             /*
9264             // Because of problems navigating from wxButton, do not go to it.
9265             if ( !wndToCheck )
9266             {
9267                 // No primary, use secondary
9268                 wndToCheck = m_wndEditor2;
9269             }
9270             // If it has editor button, focus to it after the primary editor.
9271             // NB: Doesn't work since wxButton on wxMSW doesn't seem to propagate
9272             //     key events (yes, I'm using wxWANTS_CHARS with it, and yes I
9273             //     have somewhat debugged in window.cpp itself).
9274             else if ( focused == wndToCheck &&
9275                       m_wndEditor2 &&
9276                       !(GetExtraStyle() & wxPG_EX_NO_TAB_TO_BUTTON) )
9277             {
9278                 wndToCheck = m_wndEditor2;
9279                 wxLogDebug(wxT("Exp1"));
9280             }
9281             */
9282 
9283             if ( focused != wndToCheck &&
9284                  wndToCheck )
9285             {
9286                 wndToCheck->SetFocus();
9287 
9288                 // Select all text in wxTextCtrl etc.
9289                 if ( m_wndEditor && wndToCheck == m_wndEditor )
9290                     selected->GetEditorClass()->OnFocus(selected,wndToCheck);
9291 
9292                 m_editorFocused = 1;
9293                 next = selected;
9294             }
9295         }
9296 
9297         if ( !next )
9298         {
9299             next = wxPropertyGridIterator::OneStep(m_pState, wxPG_ITERATE_VISIBLE, selected, dir);
9300 
9301             if ( next )
9302             {
9303                 // This allows preventing NavigateOut to occur
9304                 DoSelectProperty( next, wxPG_SEL_FOCUS );
9305             }
9306         }
9307     }
9308 
9309     if ( !next )
9310         event.Skip();
9311 }
9312 
9313 // -----------------------------------------------------------------------
9314 
ButtonTriggerKeyTest(wxKeyEvent & event)9315 bool wxPropertyGrid::ButtonTriggerKeyTest( wxKeyEvent &event )
9316 {
9317     int keycode = event.GetKeyCode();
9318 
9319     // Does the keycode trigger button?
9320     if ( keycode == m_pushButKeyCode &&
9321          m_wndEditor2 &&
9322          (!m_pushButKeyCodeNeedsAlt || event.AltDown()) &&
9323          (!m_pushButKeyCodeNeedsCtrl || event.ControlDown()) )
9324     {
9325         m_keyComboConsumed = 1;
9326 
9327         wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,m_wndEditor2->GetId());
9328         GetEventHandler()->AddPendingEvent(evt);
9329         return true;
9330     }
9331 
9332     return false;
9333 }
9334 
9335 // -----------------------------------------------------------------------
9336 
OnChildKeyDown(wxKeyEvent & event)9337 void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event )
9338 {
9339     int keycode = event.GetKeyCode();
9340 
9341     // Ignore Alt and Control when they are down alone
9342     if ( keycode == WXK_ALT ||
9343          keycode == WXK_CONTROL )
9344     {
9345         event.Skip();
9346         return;
9347     }
9348 
9349     if ( ButtonTriggerKeyTest(event) )
9350         return;
9351 
9352     if ( HandleChildKey(event) == true )
9353         event.Skip();
9354 
9355     GetEventHandler()->AddPendingEvent(event);
9356 }
9357 
OnChildKeyUp(wxKeyEvent & event)9358 void wxPropertyGrid::OnChildKeyUp( wxKeyEvent &event )
9359 {
9360     m_keyComboConsumed = 0;
9361 
9362     GetEventHandler()->AddPendingEvent(event);
9363 
9364     event.Skip();
9365 }
9366 
9367 // -----------------------------------------------------------------------
9368 // wxPropertyGrid miscellaneous event handling
9369 // -----------------------------------------------------------------------
9370 
OnIdle(wxIdleEvent & WXUNUSED (event))9371 void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) )
9372 {
9373     //
9374     // Check if the focus is in this control or one of its children
9375     wxWindow* newFocused = wxWindow::FindFocus();
9376 
9377     if ( newFocused != m_curFocused )
9378         HandleFocusChange( newFocused );
9379 
9380     //
9381     // Check if top-level parent has changed
9382     if ( !(GetExtraStyle() & wxPG_EX_DISABLE_TLP_TRACKING) )
9383     {
9384         wxWindow* tlp = ::wxGetTopLevelParent(this);
9385         if ( tlp != m_tlp )
9386         {
9387             OnTLPChanging(tlp);
9388         }
9389     }
9390 
9391     //
9392     // Resolve pending property removals
9393     if ( m_deletedProperties.size() > 0 )
9394     {
9395         wxArrayPGProperty& arr = m_deletedProperties;
9396         for ( unsigned int i=0; i<arr.size(); i++ )
9397         {
9398             DeleteProperty(arr[i]);
9399         }
9400         arr.clear();
9401     }
9402     if ( m_removedProperties.size() > 0 )
9403     {
9404         wxArrayPGProperty& arr = m_removedProperties;
9405         for ( unsigned int i=0; i<arr.size(); i++ )
9406         {
9407             RemoveProperty(arr[i]);
9408         }
9409         arr.clear();
9410     }
9411 }
9412 
IsEditorFocused() const9413 bool wxPropertyGrid::IsEditorFocused() const
9414 {
9415     wxWindow* focus = wxWindow::FindFocus();
9416 
9417     if ( focus == m_wndEditor || focus == m_wndEditor2 ||
9418          focus == GetEditorControl() )
9419          return true;
9420 
9421     return false;
9422 }
9423 
9424 // Called by focus event handlers. newFocused is the window that becomes focused.
HandleFocusChange(wxWindow * newFocused)9425 void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
9426 {
9427     unsigned int oldFlags = m_iFlags;
9428     bool wasEditorFocused = false;
9429     wxWindow* wndEditor = m_wndEditor;
9430 
9431     m_iFlags &= ~(wxPG_FL_FOCUSED);
9432 
9433     wxWindow* parent = newFocused;
9434 
9435     // This must be one of nextFocus' parents.
9436     while ( parent )
9437     {
9438         // Use m_eventObject, which is either wxPropertyGrid or
9439         // wxPropertyGridManager, as appropriate.
9440         if ( parent == wndEditor )
9441         {
9442             wasEditorFocused = true;
9443         }
9444         else if ( parent == m_eventObject )
9445         {
9446             m_iFlags |= wxPG_FL_FOCUSED;
9447             break;
9448         }
9449         parent = parent->GetParent();
9450     }
9451 
9452     // Notify editor control about it receiving focus
9453     if ( wasEditorFocused && m_curFocused != newFocused )
9454     {
9455         wxPGProperty* p = GetSelection();
9456         if ( p )
9457         {
9458             const wxPGEditor* editor = p->GetEditorClass();
9459             ResetEditorAppearance();
9460             editor->OnFocus(p, GetEditorControl());
9461         }
9462     }
9463 
9464     m_curFocused = newFocused;
9465 
9466     if ( (m_iFlags & wxPG_FL_FOCUSED) !=
9467          (oldFlags & wxPG_FL_FOCUSED) )
9468     {
9469         // On each focus kill, mark the next nav key event
9470         // to be ignored (can't do on set focus since the
9471         // event would occur before it).
9472         if ( !(m_iFlags & wxPG_FL_FOCUSED) )
9473         {
9474             m_iFlags |= wxPG_FL_IGNORE_NEXT_NAVKEY;
9475 
9476             // Need to store changed value
9477             CommitChangesFromEditor();
9478         }
9479         else
9480         {
9481             /*
9482             //
9483             // Preliminary code for tab-order respecting
9484             // tab-traversal (but should be moved to
9485             // OnNav handler)
9486             //
9487             wxWindow* prevFocus = event.GetWindow();
9488             wxWindow* useThis = this;
9489             if ( m_iFlags & wxPG_FL_IN_MANAGER )
9490                 useThis = GetParent();
9491 
9492             if ( prevFocus &&
9493                  prevFocus->GetParent() == useThis->GetParent() )
9494             {
9495                 wxList& children = useThis->GetParent()->GetChildren();
9496 
9497                 wxNode* node = children.Find(prevFocus);
9498 
9499                 if ( node->GetNext() &&
9500                      useThis == node->GetNext()->GetData() )
9501                     DoSelectProperty(GetFirst());
9502                 else if ( node->GetPrevious () &&
9503                           useThis == node->GetPrevious()->GetData() )
9504                     DoSelectProperty(GetLastProperty());
9505 
9506             }
9507             */
9508 
9509             m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
9510         }
9511 
9512         // Redraw selected
9513         wxPGProperty* selected = GetSelection();
9514         if ( selected && (m_iFlags & wxPG_FL_INITIALIZED) )
9515             DrawItem( selected );
9516     }
9517 }
9518 
OnFocusEvent(wxFocusEvent & event)9519 void wxPropertyGrid::OnFocusEvent( wxFocusEvent& event )
9520 {
9521     if ( event.GetEventType() == wxEVT_SET_FOCUS )
9522     {
9523         HandleFocusChange((wxWindow*)event.GetEventObject());
9524     // Line changed to "else" when applying wxPropertyGrid patch #1675902
9525     //else if ( event.GetWindow() )
9526     }
9527     else
9528     {
9529         HandleFocusChange(event.GetWindow());
9530     }
9531 
9532     event.Skip();
9533 }
9534 
9535 // -----------------------------------------------------------------------
9536 
OnChildFocusEvent(wxChildFocusEvent & event)9537 void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event )
9538 {
9539     HandleFocusChange((wxWindow*)event.GetEventObject());
9540 }
9541 
9542 // -----------------------------------------------------------------------
9543 
OnScrollEvent(wxScrollWinEvent & event)9544 void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent &event )
9545 {
9546     m_iFlags |= wxPG_FL_SCROLLED;
9547 
9548     event.Skip();
9549 }
9550 
9551 // -----------------------------------------------------------------------
9552 
OnCaptureChange(wxMouseCaptureChangedEvent & WXUNUSED (event))9553 void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent& WXUNUSED(event) )
9554 {
9555     if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
9556     {
9557         m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
9558     }
9559 }
9560 
9561 // -----------------------------------------------------------------------
9562 // Property text-based storage
9563 // -----------------------------------------------------------------------
9564 
SetAttributes(const wxPGAttributeStorage & attributes)9565 void wxPGProperty::SetAttributes( const wxPGAttributeStorage& attributes )
9566 {
9567     wxPGAttributeStorage::const_iterator it = attributes.StartIteration();
9568     wxVariant variant;
9569 
9570     while ( attributes.GetNext(it, variant) )
9571         SetAttribute( variant.GetName(), variant );
9572 }
9573 
9574 // -----------------------------------------------------------------------
9575 
9576 // Returns name of property without 'Property' at the end, and 'wx'
9577 // in the beginning (if any).
GetPropertyShortClassName(wxPGPropArg id)9578 wxString wxPropertyGridInterface::GetPropertyShortClassName( wxPGPropArg id )
9579 {
9580     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxEmptyString)
9581 
9582     if ( !p->IsCategory() )
9583     {
9584         const wxChar* src = p->GetClassName();
9585         wxString s;
9586         if ( src[0] == wxT('w') && src[1] == wxT('x') )
9587             s = &src[2];
9588         else
9589             s = src;
9590         wxASSERT( (((int)s.length())-8) > 0 );
9591         s.Truncate(s.length()-8);
9592         return s;
9593     }
9594     return wxT("Category");
9595 }
9596 
GetPropertyByNameA(wxPGPropNameStr name) const9597 wxPGProperty* wxPropertyGridInterface::GetPropertyByNameA( wxPGPropNameStr name ) const
9598 {
9599     wxPGProperty* p = GetPropertyByName(name);
9600     wxASSERT_MSG(p,wxString::Format(wxT("no property with name '%s'"),name.c_str()));
9601     return p;
9602 }
9603 
9604 // ----------------------------------------------------------------------------
9605 
PropertiesToNames(wxArrayString * names,const wxArrayPGProperty & properties) const9606 void wxPropertyGridInterface::PropertiesToNames( wxArrayString* names,
9607                                                  const wxArrayPGProperty& properties ) const
9608 {
9609     unsigned int i;
9610     for ( i=0; i<properties.size(); i++ )
9611         names->Add( properties[i]->GetName() );
9612 }
9613 
9614 // ----------------------------------------------------------------------------
9615 
NamesToProperties(wxArrayPGProperty * properties,const wxArrayString & names) const9616 void wxPropertyGridInterface::NamesToProperties( wxArrayPGProperty* properties,
9617                                                  const wxArrayString& names ) const
9618 {
9619     unsigned int i;
9620     for ( i=0; i<names.size(); i++ )
9621     {
9622         wxPGProperty* p = GetPropertyByName(names[i]);
9623         if ( p )
9624             properties->push_back(p);
9625     }
9626 }
9627 
9628 // ----------------------------------------------------------------------------
9629 // VariantDatas
9630 // ----------------------------------------------------------------------------
9631 
9632 #if !wxCHECK_VERSION(3, 0, 0)
9633 IMPLEMENT_ABSTRACT_CLASS(wxPGVariantData, wxVariantData)
9634 #endif
9635 
9636 WX_PG_IMPLEMENT_VARIANT_DATA(wxPGVariantDataPoint, wxPoint)
9637 WX_PG_IMPLEMENT_VARIANT_DATA(wxPGVariantDataSize, wxSize)
9638 WX_PG_IMPLEMENT_VARIANT_DATA(wxPGVariantDataArrayInt, wxArrayInt)
9639 WX_PG_IMPLEMENT_VARIANT_DATA(wxPGVariantDataLongLong, wxLongLong)
9640 WX_PG_IMPLEMENT_VARIANT_DATA(wxPGVariantDataULongLong, wxULongLong)
9641 
9642 WX_PG_IMPLEMENT_WXOBJECT_VARIANT_DATA_WITH_PROPER_EQ(wxPGVariantDataFont, wxFont)
9643 
9644 #ifdef __WXPYTHON__
9645 
9646 // Implement PyObject* wxVariantData manually, since it has
9647 // some needs for customization.
9648 class wxPGVariantDataPyObject : public wxPGVariantData
9649 {
9650     WX_PG_DECLARE_DYNAMIC_CLASS_VARIANTDATA(wxPGVariantDataPyObject)
9651 protected:
9652     PyObject* m_value;
9653 public:
wxPGVariantDataPyObject()9654     wxPGVariantDataPyObject()
9655     {
9656         m_value = NULL;
9657     }
wxPGVariantDataPyObject(PyObject * value)9658     wxPGVariantDataPyObject( PyObject* value )
9659     {
9660         if (!value) value = Py_None;
9661         Py_INCREF(value);
9662         m_value = value;
9663     }
~wxPGVariantDataPyObject()9664     virtual ~wxPGVariantDataPyObject()
9665     {
9666         // Avoid crashing on program exit
9667         if ( _PyThreadState_Current != NULL && m_value )
9668             Py_DECREF(m_value);
9669     }
GetValue() const9670     inline PyObject* GetValue() const { return m_value; }
GetValueRef() const9671     inline PyObject* GetValueRef() const { return m_value; }
SetValue(PyObject * value)9672     inline void SetValue(PyObject* value)
9673     {
9674         if (m_value)
9675             Py_DECREF(m_value);
9676         if (!value) value = Py_None;
9677         Py_INCREF(value);
9678         m_value = value;
9679     }
9680     // TODO
Eq(wxVariantData &) const9681     virtual bool Eq(wxVariantData&) const { return false; }
GetType() const9682     virtual wxString GetType() const { return wxT("PyObject*"); }
Clone()9683     virtual wxVariantData* Clone() { return new wxPGVariantDataPyObject(); }
Read(wxString &)9684     virtual bool Read(wxString &) { return false; }
Write(wxString &) const9685     virtual bool Write(wxString &) const { return true; }
GetDefaultValue() const9686     virtual wxVariant GetDefaultValue() const { return wxVariant( new wxPGVariantDataPyObject(NULL) ); }
9687 public:
GetValuePtr()9688     virtual void* GetValuePtr() { return (void*)m_value; }
9689 };
9690 
9691 _WX_PG_VARIANT_DATA_CLASSINFO_CONTAINER(wxPGVariantDataPyObject)
9692 
9693 WX_PG_IMPLEMENT_DYNAMIC_CLASS_VARIANTDATA(wxPGVariantDataPyObject, wxPGVariantData)
9694 
9695 PyObject* operator <<( PyObject* value, const wxVariant &variant )
9696 {
9697 	wxPGVariantDataPyObject *data = wxDynamicCastVariantData( variant.GetData(), wxPGVariantDataPyObject );
9698     wxASSERT( data );
9699     value = data->GetValue();
9700     Py_INCREF(value);
9701     return value;
9702 }
9703 
operator <<(wxVariant & variant,PyObject * value)9704 wxVariant& operator <<( wxVariant &variant, PyObject* value )
9705 {
9706     wxPGVariantDataPyObject *data = new wxPGVariantDataPyObject( value );
9707     variant.SetData( data );
9708     return variant;
9709 }
9710 
PyObjectFromVariant(const wxVariant & v)9711 PyObject* PyObjectFromVariant( const wxVariant& v )
9712 {
9713 	wxPGVariantDataPyObject *data = wxDynamicCastVariantData( v.GetData(), wxPGVariantDataPyObject );
9714     if ( !data )
9715         return NULL;
9716     PyObject* retval = data->GetValue();
9717     Py_INCREF(retval);
9718     return retval;
9719 }
9720 
PyObjectToVariant(PyObject * value)9721 wxVariant PyObjectToVariant( PyObject* value )
9722 {
9723     wxVariant variant( new wxPGVariantDataPyObject( value ) );
9724     return variant;
9725 }
9726 
9727 #endif // __WXPYTHON__
9728 
9729 
wxPG_VariantToWxObject(const wxVariant & variant,wxClassInfo * classInfo)9730 wxObject* wxPG_VariantToWxObject( const wxVariant& variant, wxClassInfo* classInfo )
9731 {
9732     if ( !variant.IsValueKindOf(classInfo) )
9733         return (wxObject*) NULL;
9734 
9735     wxVariantData* vdata = variant.GetData();
9736 
9737     wxPGVariantData* pgvdata = wxDynamicCastVariantData(vdata, wxPGVariantData);
9738     if ( pgvdata )
9739          return (wxObject*) pgvdata->GetValuePtr();
9740 
9741     if ( wxPGIsVariantClassInfo(wxPGVariantDataGetClassInfo(vdata), wxobject) )
9742         return variant.GetWxObjectPtr();
9743 
9744     return (wxObject*) NULL;
9745 }
9746 
9747 // -----------------------------------------------------------------------
9748 // wxVariant helpers
9749 // -----------------------------------------------------------------------
9750 
wxPGVariantToInt(const wxVariant & variant,long defVal)9751 long wxPGVariantToInt( const wxVariant& variant, long defVal )
9752 {
9753     if ( variant.IsNull() )
9754         return defVal;
9755 
9756     if ( wxPGIsVariantType(variant, long) )
9757         return variant.GetLong();
9758 
9759     if ( wxPGIsVariantType(variant, bool) )
9760         return variant.GetBool() ? 1 : 0;
9761 
9762 #if wxCHECK_VERSION(3, 0, 0)
9763     if ( typeid(*variant.GetData()) == typeid(wxPGVariantDataLongLong) )
9764 #else
9765     if ( wxPGVariantDataGetClassInfo(variant.GetData()) == &wxPGVariantDataLongLong::ms_classInfo )
9766 #endif
9767     {
9768         wxLongLong ll = ((const wxPGVariantDataLongLong&)variant).GetValue();
9769         if ( ll >= LONG_MAX )
9770             return LONG_MAX;
9771         else if ( ll <= LONG_MIN )
9772             return LONG_MIN;
9773         return ll.ToLong();
9774     }
9775 
9776     long l = defVal;
9777 
9778     if ( wxPGIsVariantType(variant, string) )
9779         variant.GetString().ToLong(&l, 0);
9780 
9781     return l;
9782 }
9783 
9784 // -----------------------------------------------------------------------
9785 
wxPGVariantToLongLong(const wxVariant & variant,wxLongLong_t * pResult)9786 bool wxPGVariantToLongLong( const wxVariant& variant, wxLongLong_t* pResult )
9787 {
9788     if ( variant.IsNull() )
9789         return false;
9790 
9791     if ( wxPGIsVariantType(variant, long) )
9792     {
9793         *pResult = variant.GetLong();
9794         return true;
9795     }
9796 
9797 #if wxCHECK_VERSION(3, 0, 0)
9798     if ( typeid(*variant.GetData()) == typeid(wxPGVariantDataLongLong) )
9799 #else
9800     if ( wxPGVariantDataGetClassInfo(variant.GetData()) == &wxPGVariantDataLongLong::ms_classInfo )
9801 #endif
9802     {
9803         *pResult = ((const wxPGVariantDataLongLong&)variant).GetValue().GetValue();
9804         return true;
9805     }
9806 
9807     return false;
9808 }
9809 
9810 // -----------------------------------------------------------------------
9811 
wxPGVariantToULongLong(const wxVariant & variant,wxULongLong_t * pResult)9812 bool wxPGVariantToULongLong( const wxVariant& variant, wxULongLong_t* pResult )
9813 {
9814     if ( variant.IsNull() )
9815         return false;
9816 
9817     if ( wxPGIsVariantType(variant, long) )
9818     {
9819         *pResult = (unsigned long)variant.GetLong();
9820         return true;
9821     }
9822 
9823 #if wxCHECK_VERSION(3, 0, 0)
9824     if ( typeid(*variant.GetData()) == typeid(wxPGVariantDataULongLong) )
9825 #else
9826     if ( wxPGVariantDataGetClassInfo(variant.GetData()) == &wxPGVariantDataULongLong::ms_classInfo )
9827 #endif
9828     {
9829         *pResult = ((const wxPGVariantDataULongLong&)variant).GetValue().GetValue();
9830         return true;
9831     }
9832 
9833     return false;
9834 }
9835 
9836 // -----------------------------------------------------------------------
9837 
wxPGVariantToDouble(const wxVariant & variant,double * pResult)9838 bool wxPGVariantToDouble( const wxVariant& variant, double* pResult )
9839 {
9840     if ( variant.IsNull() )
9841         return false;
9842 
9843     if ( wxPGIsVariantType(variant, double) )
9844     {
9845         *pResult = variant.GetDouble();
9846         return true;
9847     }
9848 
9849     if ( wxPGIsVariantType(variant, long) )
9850     {
9851         *pResult = (double)variant.GetLong();
9852         return true;
9853     }
9854 
9855 #if wxCHECK_VERSION(3, 0, 0)
9856     if ( typeid(*variant.GetData()) == typeid(wxPGVariantDataLongLong) )
9857 #else
9858     if ( wxPGVariantDataGetClassInfo(variant.GetData()) == &wxPGVariantDataLongLong::ms_classInfo )
9859 #endif
9860     {
9861         wxLongLong ll = ((const wxPGVariantDataLongLong&)variant).GetValue();
9862         *pResult = ll.ToDouble();
9863         return true;
9864     }
9865 
9866     if ( wxPGIsVariantType(variant, string) )
9867         if ( variant.GetString().ToDouble(pResult) )
9868             return true;
9869 
9870     return false;
9871 }
9872 
9873 // -----------------------------------------------------------------------
9874 
wxPGVariantToWxObjectPtr(const wxVariant & value,wxObject ** result)9875 bool wxPGVariantToWxObjectPtr( const wxVariant& value, wxObject** result )
9876 {
9877     wxVariantData* vdata = value.GetData();
9878 
9879     if ( !vdata->GetValueClassInfo() )
9880         return false;
9881 
9882     wxPGVariantData* pgvdata = wxDynamicCastVariantData(vdata, wxPGVariantData);
9883     if ( pgvdata )
9884     {
9885         *result = (wxObject*) pgvdata->GetValuePtr();
9886         return true;
9887     }
9888 
9889     if ( wxPGIsVariantClassInfo(wxPGVariantDataGetClassInfo(vdata),
9890                                 wxobject) )
9891     {
9892         *result = value.GetWxObjectPtr();
9893         return true;
9894     }
9895 
9896     return false;
9897 }
9898 
9899 // -----------------------------------------------------------------------
9900 // Editor class specific.
9901 
9902 // noDefCheck = true prevents infinite recursion.
RegisterEditorClass(wxPGEditor * editorclass,const wxString & name,bool noDefCheck)9903 wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorclass,
9904                                                  const wxString& name,
9905                                                  bool noDefCheck )
9906 {
9907     wxASSERT( editorclass );
9908 
9909     if ( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() )
9910         RegisterDefaultEditors();
9911 
9912     wxPGGlobalVars->m_mapEditorClasses[name] = (void*)editorclass;
9913 
9914     return editorclass;
9915 }
9916 
9917 // Registers all default editor classes
RegisterDefaultEditors()9918 void wxPropertyGrid::RegisterDefaultEditors()
9919 {
9920     wxPGRegisterDefaultEditorClass( TextCtrl );
9921     wxPGRegisterDefaultEditorClass( Choice );
9922     wxPGRegisterDefaultEditorClass( ComboBox );
9923     wxPGRegisterDefaultEditorClass( TextCtrlAndButton );
9924 #if wxPG_INCLUDE_CHECKBOX
9925     wxPGRegisterDefaultEditorClass( CheckBox );
9926 #endif
9927     wxPGRegisterDefaultEditorClass( ChoiceAndButton );
9928 
9929     // Register SpinCtrl etc. editors before use
9930     RegisterAdditionalEditors();
9931 }
9932 
GetEditorByName(const wxString & editor_name)9933 wxPGEditor* wxPropertyGridInterface::GetEditorByName( const wxString& editor_name )
9934 {
9935     wxPGEditor* editor = (wxPGEditor*) wxPGGlobalVars->m_mapEditorClasses[editor_name];
9936     wxASSERT_MSG( editor,
9937                   wxT("unregistered editor name") );
9938     return editor;
9939 }
9940 
9941 // -----------------------------------------------------------------------
9942 // wxPGAttributeStorage
9943 // -----------------------------------------------------------------------
9944 
wxPGAttributeStorage()9945 wxPGAttributeStorage::wxPGAttributeStorage()
9946 {
9947 }
9948 
~wxPGAttributeStorage()9949 wxPGAttributeStorage::~wxPGAttributeStorage()
9950 {
9951     wxPGHashMapS2P::iterator it;
9952 
9953     for ( it = m_map.begin(); it != m_map.end(); it++ )
9954     {
9955         wxVariantData* data = (wxVariantData*) it->second;
9956         data->DecRef();
9957     }
9958 }
9959 
Set(const wxString & name,const wxVariant & value)9960 void wxPGAttributeStorage::Set( const wxString& name, const wxVariant& value )
9961 {
9962     wxVariantData* data = value.GetData();
9963 
9964     // Free old, if any
9965     wxPGHashMapS2P::iterator it = m_map.find(name);
9966     if ( it != m_map.end() )
9967     {
9968         ((wxVariantData*)it->second)->DecRef();
9969 
9970         if ( !data )
9971         {
9972             // If Null variant, just remove from set
9973             m_map.erase(it);
9974             return;
9975         }
9976     }
9977 
9978     if ( data )
9979     {
9980         data->IncRef();
9981 
9982         m_map[name] = data;
9983     }
9984 }
9985 
9986 // -----------------------------------------------------------------------
9987 // wxPGStringTokenizer
9988 //   Needed to handle C-style string lists (e.g. "str1" "str2")
9989 // -----------------------------------------------------------------------
9990 
wxPGStringTokenizer(const wxString & str,wxChar delimeter)9991 wxPGStringTokenizer::wxPGStringTokenizer( const wxString& str, wxChar delimeter )
9992     : m_str(&str), m_curPos(str.begin()), m_delimeter(delimeter)
9993 {
9994 }
9995 
~wxPGStringTokenizer()9996 wxPGStringTokenizer::~wxPGStringTokenizer()
9997 {
9998 }
9999 
HasMoreTokens()10000 bool wxPGStringTokenizer::HasMoreTokens()
10001 {
10002     const wxString& str = *m_str;
10003 
10004     wxString::const_iterator i = m_curPos;
10005 
10006     wxUniChar delim = m_delimeter;
10007     wxUniChar a;
10008     wxUniChar prev_a = wxT('\0');
10009 
10010     bool inToken = false;
10011 
10012     while ( i != str.end() )
10013     {
10014         a = wxPGGetIterChar(str, i);
10015 
10016         if ( !inToken )
10017         {
10018             if ( a == delim )
10019             {
10020                 inToken = true;
10021                 m_readyToken.clear();
10022             }
10023         }
10024         else
10025         {
10026             if ( prev_a != wxT('\\') )
10027             {
10028                 if ( a != delim )
10029                 {
10030                     if ( a != wxT('\\') )
10031                         m_readyToken << a;
10032                 }
10033                 else
10034                 {
10035                     i++;
10036                     m_curPos = i;
10037                     return true;
10038                 }
10039                 prev_a = a;
10040             }
10041             else
10042             {
10043                 m_readyToken << a;
10044                 prev_a = wxT('\0');
10045             }
10046         }
10047         i++;
10048     }
10049 
10050     m_curPos = str.end();
10051 
10052     if ( inToken )
10053         return true;
10054 
10055     return false;
10056 }
10057 
GetNextToken()10058 wxString wxPGStringTokenizer::GetNextToken()
10059 {
10060     return m_readyToken;
10061 }
10062 
10063 // -----------------------------------------------------------------------
10064 // wxPGChoiceEntry
10065 // -----------------------------------------------------------------------
10066 
wxPGChoiceEntry()10067 wxPGChoiceEntry::wxPGChoiceEntry()
10068     : wxPGCell(), m_value(wxPG_INVALID_VALUE)
10069 {
10070 }
10071 
wxPGChoiceEntry(const wxPGChoiceEntry & entry)10072 wxPGChoiceEntry::wxPGChoiceEntry( const wxPGChoiceEntry& entry )
10073     : wxPGCell( entry.GetText(), entry.GetBitmap(),
10074         entry.GetFgCol(), entry.GetBgCol() ), m_value(entry.GetValue())
10075 {
10076 }
10077 
10078 // -----------------------------------------------------------------------
10079 // wxPGChoicesData
10080 // -----------------------------------------------------------------------
10081 
wxPGChoicesData()10082 wxPGChoicesData::wxPGChoicesData()
10083 {
10084     m_refCount = 1;
10085 }
10086 
~wxPGChoicesData()10087 wxPGChoicesData::~wxPGChoicesData()
10088 {
10089     Clear();
10090 }
10091 
Clear()10092 void wxPGChoicesData::Clear()
10093 {
10094     unsigned int i;
10095 
10096     for ( i=0; i<m_items.size(); i++ )
10097     {
10098         delete Item(i);
10099     }
10100 
10101 #if wxPG_USE_STL
10102     m_items.resize(0);
10103 #else
10104     m_items.Empty();
10105 #endif
10106 }
10107 
CopyDataFrom(wxPGChoicesData * data)10108 void wxPGChoicesData::CopyDataFrom( wxPGChoicesData* data )
10109 {
10110     wxASSERT( m_items.size() == 0 );
10111 
10112     unsigned int i;
10113 
10114     for ( i=0; i<data->GetCount(); i++ )
10115         m_items.push_back( new wxPGChoiceEntry(*data->Item(i)) );
10116 }
10117 
10118 // -----------------------------------------------------------------------
10119 // wxPGChoices
10120 // -----------------------------------------------------------------------
10121 
Add(const wxString & label,int value)10122 wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, int value )
10123 {
10124     EnsureData();
10125 
10126     wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
10127     m_data->Insert( -1, p );
10128     return *p;
10129 }
10130 
10131 // -----------------------------------------------------------------------
10132 
Add(const wxString & label,const wxBitmap & bitmap,int value)10133 wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, const wxBitmap& bitmap, int value )
10134 {
10135     EnsureData();
10136 
10137     wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
10138     p->SetBitmap(bitmap);
10139     m_data->Insert( -1, p );
10140     return *p;
10141 }
10142 
10143 // -----------------------------------------------------------------------
10144 
Insert(const wxPGChoiceEntry & entry,int index)10145 wxPGChoiceEntry& wxPGChoices::Insert( const wxPGChoiceEntry& entry, int index )
10146 {
10147     EnsureData();
10148 
10149     wxPGChoiceEntry* p = new wxPGChoiceEntry(entry);
10150     m_data->Insert(index, p);
10151     return *p;
10152 }
10153 
10154 // -----------------------------------------------------------------------
10155 
Insert(const wxString & label,int index,int value)10156 wxPGChoiceEntry& wxPGChoices::Insert( const wxString& label, int index, int value )
10157 {
10158     EnsureData();
10159 
10160     wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
10161     m_data->Insert( index, p );
10162     return *p;
10163 }
10164 
10165 // -----------------------------------------------------------------------
10166 
AddAsSorted(const wxString & label,int value)10167 wxPGChoiceEntry& wxPGChoices::AddAsSorted( const wxString& label, int value )
10168 {
10169     EnsureData();
10170 
10171     size_t index = 0;
10172 
10173     while ( index < GetCount() )
10174     {
10175         int cmpRes = GetLabel(index).Cmp(label);
10176         if ( cmpRes > 0 )
10177             break;
10178         index++;
10179     }
10180 
10181     wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
10182     m_data->Insert( index, p );
10183     return *p;
10184 }
10185 
10186 // -----------------------------------------------------------------------
10187 
Add(const wxChar ** labels,const ValArrItem * values)10188 void wxPGChoices::Add( const wxChar** labels, const ValArrItem* values )
10189 {
10190     EnsureData();
10191 
10192     unsigned int itemcount = 0;
10193     const wxChar** p = &labels[0];
10194     while ( *p ) { p++; itemcount++; }
10195 
10196     unsigned int i;
10197     for ( i = 0; i < itemcount; i++ )
10198     {
10199         int value = wxPG_INVALID_VALUE;
10200         if ( values )
10201             value = values[i];
10202         m_data->Insert( -1, new wxPGChoiceEntry(labels[i], value) );
10203     }
10204 }
10205 
10206 // -----------------------------------------------------------------------
10207 
Add(const wxArrayString & arr,const ValArrItem * values)10208 void wxPGChoices::Add( const wxArrayString& arr, const ValArrItem* values )
10209 {
10210     EnsureData();
10211 
10212     unsigned int i;
10213     unsigned int itemcount = arr.GetCount();
10214 
10215     for ( i = 0; i < itemcount; i++ )
10216     {
10217         int value = wxPG_INVALID_VALUE;
10218         if ( values )
10219             value = values[i];
10220         m_data->Insert( -1, new wxPGChoiceEntry(arr[i], value) );
10221     }
10222 }
10223 
10224 // -----------------------------------------------------------------------
10225 
Add(const wxArrayString & arr,const wxArrayInt & arrint)10226 void wxPGChoices::Add( const wxArrayString& arr, const wxArrayInt& arrint )
10227 {
10228     EnsureData();
10229 
10230     unsigned int i;
10231     unsigned int itemcount = arr.GetCount();
10232 
10233     for ( i = 0; i < itemcount; i++ )
10234     {
10235         int value = wxPG_INVALID_VALUE;
10236         if ( arrint.size() )
10237             value = arrint[i];
10238         m_data->Insert( -1, new wxPGChoiceEntry(arr[i], value) );
10239     }
10240 }
10241 
10242 // -----------------------------------------------------------------------
10243 
RemoveAt(size_t nIndex,size_t count)10244 void wxPGChoices::RemoveAt(size_t nIndex, size_t count)
10245 {
10246     wxASSERT( m_data->m_refCount != 0xFFFFFFF );
10247     unsigned int i;
10248     for ( i=nIndex; i<(nIndex+count); i++)
10249         delete m_data->Item(i);
10250     m_data->m_items.RemoveAt(nIndex, count);
10251 }
10252 
10253 // -----------------------------------------------------------------------
10254 
Index(const wxString & str) const10255 int wxPGChoices::Index( const wxString& str ) const
10256 {
10257     if ( IsOk() )
10258     {
10259         unsigned int i;
10260         for ( i=0; i< m_data->GetCount(); i++ )
10261         {
10262             if ( m_data->Item(i)->GetText() == str )
10263                 return i;
10264         }
10265     }
10266     return -1;
10267 }
10268 
10269 // -----------------------------------------------------------------------
10270 
Index(int val) const10271 int wxPGChoices::Index( int val ) const
10272 {
10273     if ( IsOk() )
10274     {
10275         unsigned int i;
10276         for ( i=0; i< m_data->GetCount(); i++ )
10277         {
10278             if ( m_data->Item(i)->GetValue() == val )
10279                 return i;
10280         }
10281     }
10282     return -1;
10283 }
10284 
10285 // -----------------------------------------------------------------------
10286 
GetLabels() const10287 wxArrayString wxPGChoices::GetLabels() const
10288 {
10289     wxArrayString arr;
10290     unsigned int i;
10291 
10292     if ( IsOk() )
10293         for ( i=0; i<GetCount(); i++ )
10294             arr.push_back(GetLabel(i));
10295 
10296     return arr;
10297 }
10298 
10299 // -----------------------------------------------------------------------
10300 #if wxPG_COMPATIBILITY_1_2_0
HasValue(unsigned int WXUNUSED (i)) const10301 bool wxPGChoices::HasValue( unsigned int WXUNUSED(i) ) const
10302 {
10303     return true;
10304 }
10305 
10306 // -----------------------------------------------------------------------
10307 
HasValues() const10308 bool wxPGChoices::HasValues() const
10309 {
10310     return true;
10311 }
10312 #endif
10313 
10314 // -----------------------------------------------------------------------
10315 
GetValuesForStrings(const wxArrayString & strings) const10316 wxArrayInt wxPGChoices::GetValuesForStrings( const wxArrayString& strings ) const
10317 {
10318     wxArrayInt arr;
10319 
10320     if ( IsOk() )
10321     {
10322         unsigned int i;
10323         for ( i=0; i< strings.size(); i++ )
10324         {
10325             int index = Index(strings[i]);
10326             if ( index >= 0 )
10327                 arr.Add(GetValue(index));
10328             else
10329                 arr.Add(wxPG_INVALID_VALUE);
10330         }
10331     }
10332 
10333     return arr;
10334 }
10335 
10336 // -----------------------------------------------------------------------
10337 
GetIndicesForStrings(const wxArrayString & strings,wxArrayString * unmatched) const10338 wxArrayInt wxPGChoices::GetIndicesForStrings( const wxArrayString& strings,
10339                                               wxArrayString* unmatched ) const
10340 {
10341     wxArrayInt arr;
10342 
10343     if ( IsOk() )
10344     {
10345         unsigned int i;
10346         for ( i=0; i< strings.size(); i++ )
10347         {
10348             const wxString& str = strings[i];
10349             int index = Index(str);
10350             if ( index >= 0 )
10351                 arr.Add(index);
10352             else if ( unmatched )
10353                 unmatched->Add(str);
10354         }
10355     }
10356 
10357     return arr;
10358 }
10359 
10360 // -----------------------------------------------------------------------
10361 
AssignData(wxPGChoicesData * data)10362 void wxPGChoices::AssignData( wxPGChoicesData* data )
10363 {
10364     Free();
10365 
10366     if ( data != wxPGChoicesEmptyData )
10367     {
10368         m_data = data;
10369         data->m_refCount++;
10370     }
10371 }
10372 
10373 // -----------------------------------------------------------------------
10374 
Init()10375 void wxPGChoices::Init()
10376 {
10377     m_data = wxPGChoicesEmptyData;
10378 }
10379 
10380 // -----------------------------------------------------------------------
10381 
Free()10382 void wxPGChoices::Free()
10383 {
10384     if ( m_data != wxPGChoicesEmptyData )
10385     {
10386         m_data->DecRef();
10387         m_data = wxPGChoicesEmptyData;
10388     }
10389 }
10390 
10391 // -----------------------------------------------------------------------
10392 // wxPropertyGridEvent
10393 // -----------------------------------------------------------------------
10394 
IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent,wxCommandEvent)10395 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent, wxCommandEvent)
10396 
10397 
10398 DEFINE_EVENT_TYPE( wxEVT_PG_SELECTED )
10399 DEFINE_EVENT_TYPE( wxEVT_PG_CHANGING )
10400 DEFINE_EVENT_TYPE( wxEVT_PG_CHANGED )
10401 DEFINE_EVENT_TYPE( wxEVT_PG_HIGHLIGHTED )
10402 DEFINE_EVENT_TYPE( wxEVT_PG_RIGHT_CLICK )
10403 DEFINE_EVENT_TYPE( wxEVT_PG_PAGE_CHANGED )
10404 DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_EXPANDED )
10405 DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_COLLAPSED )
10406 DEFINE_EVENT_TYPE( wxEVT_PG_DOUBLE_CLICK )
10407 DEFINE_EVENT_TYPE( wxEVT_PG_LABEL_EDIT_BEGIN )
10408 DEFINE_EVENT_TYPE( wxEVT_PG_LABEL_EDIT_ENDING )
10409 
10410 
10411 // -----------------------------------------------------------------------
10412 
10413 void wxPropertyGridEvent::Init()
10414 {
10415     m_validationInfo = NULL;
10416     m_column = 1;
10417     m_canVeto = false;
10418     m_wasVetoed = false;
10419 }
10420 
10421 // -----------------------------------------------------------------------
10422 
wxPropertyGridEvent(wxEventType commandType,int id)10423 wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType, int id)
10424     : wxCommandEvent(commandType,id)
10425 {
10426     m_property = NULL;
10427     Init();
10428 }
10429 
10430 // -----------------------------------------------------------------------
10431 
wxPropertyGridEvent(const wxPropertyGridEvent & event)10432 wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent& event)
10433     : wxCommandEvent(event)
10434 {
10435     m_eventType = event.GetEventType();
10436     m_eventObject = event.m_eventObject;
10437     m_pg = event.m_pg;
10438     m_property = event.m_property;
10439     m_validationInfo = event.m_validationInfo;
10440     m_canVeto = event.m_canVeto;
10441     m_wasVetoed = event.m_wasVetoed;
10442 }
10443 
10444 // -----------------------------------------------------------------------
10445 
~wxPropertyGridEvent()10446 wxPropertyGridEvent::~wxPropertyGridEvent()
10447 {
10448 }
10449 
10450 // -----------------------------------------------------------------------
10451 
Clone() const10452 wxEvent* wxPropertyGridEvent::Clone() const
10453 {
10454     return new wxPropertyGridEvent( *this );
10455 }
10456 
10457 // -----------------------------------------------------------------------
10458 // wxPropertyGridInterface
10459 // - common methods for wxPropertyGrid and wxPropertyGridManager -
10460 // -----------------------------------------------------------------------
10461 
DoSetPropertyAttribute(wxPGPropArg id,const wxString & name,wxVariant & value,long argFlags)10462 void wxPropertyGridInterface::DoSetPropertyAttribute( wxPGPropArg id, const wxString& name,
10463                                                       wxVariant& value, long argFlags )
10464 {
10465     wxPG_PROP_ARG_CALL_PROLOG()
10466 
10467     p->SetAttribute( name, value );
10468 
10469     if ( argFlags & wxPG_RECURSE )
10470     {
10471         unsigned int i;
10472         for ( i = 0; i < p->GetChildCount(); i++ )
10473             DoSetPropertyAttribute(p->Item(i), name, value, argFlags);
10474     }
10475 }
10476 
SetPropertyAttributeAll(const wxString & attrName,wxVariant value)10477 void wxPropertyGrid::SetPropertyAttributeAll( const wxString& attrName, wxVariant value )
10478 {
10479     DoSetPropertyAttribute(GetRoot(), attrName, value, wxPG_RECURSE);
10480 }
10481 
10482 // -----------------------------------------------------------------------
10483 
GetPropertiesWithFlag(wxArrayPGProperty * targetArr,wxPGProperty::FlagType flags,bool inverse,int iterFlags) const10484 void wxPropertyGridInterface::GetPropertiesWithFlag( wxArrayPGProperty* targetArr,
10485                                                      wxPGProperty::FlagType flags,
10486                                                      bool inverse,
10487                                                      int iterFlags ) const
10488 {
10489     wxASSERT( targetArr );
10490     wxPGVIterator it = GetVIterator( iterFlags );
10491 
10492     for ( ;
10493           !it.AtEnd();
10494           it.Next() )
10495     {
10496         const wxPGProperty* property = it.GetProperty();
10497 
10498         if ( !inverse )
10499         {
10500             if ( (property->GetFlags() & flags) == flags )
10501                 targetArr->push_back((wxPGProperty*)property);
10502         }
10503         else
10504         {
10505             if ( (property->GetFlags() & flags) != flags )
10506                 targetArr->push_back((wxPGProperty*)property);
10507         }
10508     }
10509 }
10510 
10511 // -----------------------------------------------------------------------
10512 
SetPropertiesFlag(const wxArrayPGProperty & srcArr,wxPGProperty::FlagType flags,bool inverse)10513 void wxPropertyGridInterface::SetPropertiesFlag( const wxArrayPGProperty& srcArr,
10514                                                  wxPGProperty::FlagType flags,
10515                                                  bool inverse )
10516 {
10517     unsigned int i;
10518 
10519     for ( i=0; i<srcArr.size(); i++ )
10520     {
10521         wxPGProperty* property = srcArr[i];
10522 
10523         if ( !inverse )
10524             property->SetFlag(flags);
10525         else
10526             property->ClearFlag(flags);
10527     }
10528 
10529     // If collapsed flag or hidden was manipulated, we need to update virtual
10530     // size.
10531     wxPropertyGrid* pg = GetPropertyGrid();
10532     if ( flags & (wxPG_PROP_COLLAPSED|wxPG_PROP_HIDDEN) )
10533     {
10534         GetState()->VirtualHeightChanged();
10535         pg->RecalculateVirtualSize();
10536     }
10537 }
10538 
10539 // -----------------------------------------------------------------------
10540 
SetBoolChoices(const wxString & trueChoice,const wxString & falseChoice)10541 void wxPropertyGridInterface::SetBoolChoices( const wxString& trueChoice,
10542                                               const wxString& falseChoice )
10543 {
10544     wxPGGlobalVars->m_boolChoices[0] = falseChoice;
10545     wxPGGlobalVars->m_boolChoices[1] = trueChoice;
10546 }
10547 
10548 // -----------------------------------------------------------------------
10549 
10550 wxPGChoices gs_emptyChoices;
10551 
GetPropertyChoices(wxPGPropArg id)10552 wxPGChoices& wxPropertyGridInterface::GetPropertyChoices( wxPGPropArg id )
10553 {
10554     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(gs_emptyChoices)
10555 
10556     wxPGChoiceInfo ci;
10557     ci.m_choices = (wxPGChoices*) NULL;
10558 
10559     p->GetChoiceInfo(&ci);
10560 
10561     if ( !ci.m_choices )
10562         return gs_emptyChoices;
10563 
10564     return *ci.m_choices;
10565 }
10566 
10567 // -----------------------------------------------------------------------
10568 
DoGetPropertyByName(wxPGPropNameStr name) const10569 wxPGProperty* wxPropertyGridInterface::DoGetPropertyByName( wxPGPropNameStr name ) const
10570 {
10571     return m_pState->BaseGetPropertyByName(name);
10572 }
10573 
10574 // -----------------------------------------------------------------------
10575 
GetPropertyByName(wxPGPropNameStr name,wxPGPropNameStr subname) const10576 wxPGProperty* wxPropertyGridInterface::GetPropertyByName( wxPGPropNameStr name,
10577                                                           wxPGPropNameStr subname ) const
10578 {
10579     wxPGProperty* p = DoGetPropertyByName(name);
10580     if ( !p || !p->GetChildCount() )
10581         return wxNullProperty;
10582 
10583     return p->GetPropertyByName(subname);
10584 }
10585 
10586 // -----------------------------------------------------------------------
10587 
10588 // Since GetPropertyByName is used *a lot*, this makes sense
10589 // since non-virtual method can be called with less code.
GetPropertyByName(wxPGPropNameStr name) const10590 wxPGProperty* wxPropertyGridInterface::GetPropertyByName( wxPGPropNameStr name ) const
10591 {
10592     wxPGProperty* p = DoGetPropertyByName(name);
10593     if ( p )
10594         return p;
10595 
10596     // Check if its "Property.SubProperty" format
10597     int pos = name.Find(wxT('.'));
10598     if ( pos <= 0 )
10599         return NULL;
10600 
10601     return GetPropertyByName(name.substr(0,pos),
10602                              name.substr(pos+1,name.length()-pos-1));
10603 }
10604 
10605 // -----------------------------------------------------------------------
10606 
HideProperty(wxPGPropArg id,bool hide,int flags)10607 bool wxPropertyGridInterface::HideProperty( wxPGPropArg id, bool hide, int flags )
10608 {
10609     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
10610 
10611     wxPropertyGrid* pg = m_pState->GetGrid();
10612 
10613     if ( pg == p->GetGrid() )
10614         return pg->DoHideProperty(p, hide, flags);
10615     else
10616         m_pState->DoHideProperty(p, hide, flags);
10617 
10618     return true;
10619 }
10620 
10621 // -----------------------------------------------------------------------
10622 
Collapse(wxPGPropArg id)10623 bool wxPropertyGridInterface::Collapse( wxPGPropArg id )
10624 {
10625     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
10626     wxPropertyGrid* pg = p->GetGridIfDisplayed();
10627     if ( pg )
10628         return pg->DoCollapse(p, true /* C::B patch */);
10629 
10630     return p->GetParentState()->DoCollapse(p);
10631 }
10632 
10633 // -----------------------------------------------------------------------
10634 
Expand(wxPGPropArg id)10635 bool wxPropertyGridInterface::Expand( wxPGPropArg id )
10636 {
10637     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
10638     wxPropertyGrid* pg = p->GetGridIfDisplayed();
10639     if ( pg )
10640         return pg->DoExpand(p, true /* C::B patch */);
10641 
10642     return p->GetParentState()->DoExpand(p);
10643 }
10644 
10645 // -----------------------------------------------------------------------
10646 
SetPropertyLabel(wxPGPropArg id,const wxString & newproplabel)10647 void wxPropertyGridInterface::SetPropertyLabel( wxPGPropArg id, const wxString& newproplabel )
10648 {
10649     wxPG_PROP_ARG_CALL_PROLOG()
10650 
10651     p->SetLabel( newproplabel );
10652 
10653     wxPropertyGridState* state = p->GetParentState();
10654     wxPropertyGrid* pg = state->GetGrid();
10655 
10656     if ( pg->HasFlag(wxPG_AUTO_SORT) )
10657         pg->Sort(p->GetParent());
10658 
10659     if ( pg->GetState() == state )
10660     {
10661         if ( pg->HasFlag(wxPG_AUTO_SORT) )
10662             pg->Refresh();
10663         else
10664             pg->DrawItem( p );
10665     }
10666 }
10667 
10668 // -----------------------------------------------------------------------
10669 
SetPropertyMaxLength(wxPGPropArg id,int maxLen)10670 bool wxPropertyGridInterface::SetPropertyMaxLength( wxPGPropArg id, int maxLen )
10671 {
10672     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
10673 
10674     wxPropertyGrid* pg = m_pState->GetGrid();
10675 
10676     p->m_maxLen = (short) maxLen;
10677 
10678     // Adjust control if selected currently
10679     if ( pg == p->GetGrid() && p == m_pState->GetSelection() )
10680     {
10681         wxWindow* wnd = pg->GetEditorControl();
10682         wxTextCtrl* tc = wxDynamicCast(wnd,wxTextCtrl);
10683         if ( tc )
10684             tc->SetMaxLength( maxLen );
10685         else
10686         // Not a text ctrl
10687             return false;
10688     }
10689 
10690     return true;
10691 }
10692 
10693 // -----------------------------------------------------------------------
10694 // GetPropertyValueAsXXX methods
10695 
10696 #define IMPLEMENT_GET_VALUE(T,TRET,BIGNAME,DEFRETVAL) \
10697 TRET wxPropertyGridInterface::GetPropertyValueAs##BIGNAME( wxPGPropArg id ) const \
10698 { \
10699     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(DEFRETVAL) \
10700     wxVariant value = p->GetValue(); \
10701     if ( wxStrcmp(value.GetType(), wxPGTypeName_##T) != 0 ) \
10702     { \
10703         wxPGGetFailed(p,wxPGTypeName_##T); \
10704         return (TRET)DEFRETVAL; \
10705     } \
10706     return (TRET)value.Get##BIGNAME(); \
10707 }
10708 
10709 // String is different than others.
GetPropertyValueAsString(wxPGPropArg id) const10710 wxString wxPropertyGridInterface::GetPropertyValueAsString( wxPGPropArg id ) const
10711 {
10712     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxEmptyString)
10713     return p->GetValueAsString(wxPG_FULL_VALUE);
10714 }
10715 
GetPropertyValueAsBool(wxPGPropArg id) const10716 bool wxPropertyGridInterface::GetPropertyValueAsBool( wxPGPropArg id ) const
10717 {
10718     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
10719     wxVariant value = p->GetValue();
10720     if ( wxStrcmp(value.GetType(), wxPGTypeName_bool) == 0 )
10721     {
10722         return value.GetBool();
10723     }
10724     if ( wxStrcmp(value.GetType(), wxPGTypeName_long) == 0 )
10725     {
10726         return value.GetLong()?true:false;
10727     }
10728     wxPGGetFailed(p,wxPGTypeName_bool);
10729     return false;
10730 }
10731 
10732 IMPLEMENT_GET_VALUE(long,long,Long,0)
10733 IMPLEMENT_GET_VALUE(double,double,Double,0.0)
IMPLEMENT_GET_VALUE(void,void *,VoidPtr,NULL)10734 IMPLEMENT_GET_VALUE(void,void*,VoidPtr,NULL)
10735 
10736 #ifdef __WXPYTHON__
10737 PyObject* wxPropertyGridInterface::GetPropertyValueAsPyObject( wxPGPropArg id ) const
10738 {
10739     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(NULL)
10740     wxVariant value = p->GetValue();
10741 
10742     wxPGVariantDataPyObject* pgvdata = wxDynamicCastVariantData(value.GetData(), wxPGVariantDataPyObject);
10743     if ( value.IsNull() || !pgvdata )
10744     {
10745         wxPGGetFailed(p,wxT("PyObject*"));
10746         return NULL;
10747     }
10748     return PyObjectFromVariant(value);
10749 }
10750 #endif
10751 
10752 // wxObject is different than others.
10753 wxObject*
GetPropertyValueAsWxObjectPtr(wxPGPropArg id) const10754 wxPropertyGridInterface::GetPropertyValueAsWxObjectPtr( wxPGPropArg id ) const
10755 {
10756     wxPG_PROP_ARG_CALL_PROLOG_RETVAL((wxObject*)NULL)
10757     wxVariant variant = p->GetValue();
10758     wxObject* result;
10759     if ( wxPGVariantToWxObjectPtr(variant, &result) )
10760         return result;
10761     return NULL;
10762 }
10763 
10764 // -----------------------------------------------------------------------
10765 
IsPropertyExpanded(wxPGPropArg id) const10766 bool wxPropertyGridInterface::IsPropertyExpanded( wxPGPropArg id ) const
10767 {
10768     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
10769     return p->IsExpanded();
10770 }
10771 
10772 // -----------------------------------------------------------------------
10773 
CreatePropertyByType(const wxString &,const wxString &,const wxString &)10774 wxPGProperty* wxPropertyGridInterface::CreatePropertyByType(const wxString &/*valuetype*/,
10775                                                             const wxString &/*label*/,
10776                                                             const wxString &/*name*/)
10777 {
10778     //
10779     // TODO: This code (possibly) needs to be ported to wxPropertyGrid 1.3+
10780     //
10781     /*
10782     wxPGHashMapS2P::iterator it;
10783 
10784     it = wxPGGlobalVars->m_dictValueType.find(valuetype);
10785 
10786     if ( it != wxPGGlobalVars->m_dictValueType.end() )
10787     {
10788         wxPGValueType* vt = (wxPGValueType*) it->second;
10789         wxPGProperty* p = vt->GenerateProperty(label,name);
10790     #ifdef __WXDEBUG__
10791         if ( !p )
10792         {
10793             wxLogDebug(wxT("WARNING: CreatePropertyByValueType generated NULL property for ValueType \"%s\""),valuetype.c_str());
10794             return (wxPGProperty*) NULL;
10795         }
10796     #endif
10797         return p;
10798     }
10799 
10800     wxLogDebug(wxT("WARNING: No value type registered with name \"%s\""),valuetype.c_str());
10801     */
10802     return (wxPGProperty*) NULL;
10803 }
10804 
10805 // -----------------------------------------------------------------------
10806 
CreatePropertyByClass(const wxString &,const wxString &,const wxString &)10807 wxPGProperty* wxPropertyGridInterface::CreatePropertyByClass(const wxString &/*classname*/,
10808                                                              const wxString &/*label*/,
10809                                                              const wxString &/*name*/)
10810 {
10811     //
10812     // TODO: This code (possibly) needs to be ported to wxPropertyGrid 1.3+
10813     //
10814     /*
10815     wxPGHashMapS2P* cis =
10816         (wxPGHashMapS2P*) &wxPGGlobalVars->m_dictPropertyClassInfo;
10817 
10818     const wxString* pClassname = &classname;
10819     wxString s;
10820 
10821     // Translate to long name, if necessary
10822     if ( (pClassname->GetChar(0) != wxT('w') || pClassname->GetChar(1) != wxT('x')) &&
10823           pClassname->Find(wxT("Property")) < 0 )
10824     {
10825         if ( classname != wxT("Category") )
10826             s.Printf(wxT("wx%sProperty"),pClassname->c_str());
10827         else
10828             s = wxT("wxPropertyCategory");
10829         pClassname = &s;
10830     }
10831 
10832     wxPGHashMapS2P::iterator it;
10833     it = cis->find(*pClassname);
10834 
10835     if ( it != cis->end() )
10836     {
10837         wxPGPropertyClassInfo* pci = (wxPGPropertyClassInfo*) it->second;
10838         wxPGProperty* p = pci->m_constructor(label,name);
10839         return p;
10840     }
10841     wxLogError(wxT("No such property class: %s"),pClassname->c_str());
10842     */
10843     return (wxPGProperty*) NULL;
10844 }
10845 
10846 // -----------------------------------------------------------------------
10847 // wxPropertyGridInterface wrappers
10848 // -----------------------------------------------------------------------
10849 
ChangePropertyValue(wxPGPropArg id,wxVariant newValue)10850 bool wxPropertyGridInterface::ChangePropertyValue( wxPGPropArg id, wxVariant newValue )
10851 {
10852     return GetPropertyGrid()->ChangePropertyValue(id, newValue);
10853 }
10854 
10855 // -----------------------------------------------------------------------
10856 // wxPropertyGridIterator
10857 // -----------------------------------------------------------------------
10858 
Init(wxPropertyGridState * state,int flags,wxPGProperty * property,int dir)10859 void wxPropertyGridIteratorBase::Init( wxPropertyGridState* state, int flags, wxPGProperty* property, int dir  )
10860 {
10861     wxASSERT( dir == 1 || dir == -1 );
10862 
10863     m_state = state;
10864     m_baseParent = state->DoGetRoot();
10865     if ( !property && m_baseParent->GetChildCount() )
10866         property = m_baseParent->Item(0);
10867 
10868     m_property = property;
10869 
10870     wxPG_ITERATOR_CREATE_MASKS(flags, m_itemExMask, m_parentExMask)
10871 
10872     // Need to skip first?
10873     if ( property && (property->GetFlags() & m_itemExMask) )
10874     {
10875         if ( dir == 1 )
10876             Next();
10877         else
10878             Prev();
10879     }
10880 }
10881 
Init(wxPropertyGridState * state,int flags,int startPos,int dir)10882 void wxPropertyGridIteratorBase::Init( wxPropertyGridState* state, int flags, int startPos, int dir  )
10883 {
10884     wxPGProperty* property;
10885 
10886     if ( startPos == wxTOP )
10887     {
10888         property = NULL;
10889         if ( dir == 0 )
10890             dir = 1;
10891     }
10892     else if ( startPos == wxBOTTOM )
10893     {
10894         property = state->GetLastItem(flags);
10895         if ( dir == 0 )
10896             dir = -1;
10897     }
10898     else
10899     {
10900         wxASSERT_MSG( false, wxT("Only supported stating positions are wxTOP and wxBOTTOM") );
10901         property = NULL;
10902     }
10903 
10904     Init( state, flags, property, dir );
10905 }
10906 
Assign(const wxPropertyGridIteratorBase & it)10907 void wxPropertyGridIteratorBase::Assign( const wxPropertyGridIteratorBase& it )
10908 {
10909     m_property = it.m_property;
10910     m_state = it.m_state;
10911     m_baseParent = it.m_baseParent;
10912     m_itemExMask = it.m_itemExMask;
10913     m_parentExMask = it.m_parentExMask;
10914 }
10915 
Prev()10916 void wxPropertyGridIteratorBase::Prev()
10917 {
10918     wxPGProperty* property = m_property;
10919     if ( !property )
10920         return;
10921 
10922     wxPGProperty* parent = property->GetParent();
10923     wxASSERT( parent );
10924     unsigned int index = property->GetIndexInParent();
10925 
10926     if ( index > 0 )
10927     {
10928         // Previous sibling
10929         index--;
10930 
10931         property = parent->Item(index);
10932 
10933         // Go to last children?
10934         if ( property->GetChildCount() &&
10935              wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) )
10936         {
10937             // First child
10938             property = property->Last();
10939         }
10940     }
10941     else
10942     {
10943         // Up to a parent
10944         if ( parent == m_baseParent )
10945         {
10946             m_property = NULL;
10947             return;
10948         }
10949         else
10950         {
10951             property = parent;
10952         }
10953     }
10954 
10955     m_property = property;
10956 
10957     // If property does not match our criteria, skip it
10958     if ( property->GetFlags() & m_itemExMask )
10959         Prev();
10960 }
10961 
Next(bool iterateChildren)10962 void wxPropertyGridIteratorBase::Next( bool iterateChildren )
10963 {
10964     wxPGProperty* property = m_property;
10965     if ( !property )
10966         return;
10967 
10968     if ( property->GetChildCount() &&
10969          wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) &&
10970          iterateChildren )
10971     {
10972         // First child
10973         property = property->Item(0);
10974     }
10975     else
10976     {
10977         wxPGProperty* parent = property->GetParent();
10978         wxASSERT( parent );
10979         unsigned int index = property->GetIndexInParent() + 1;
10980 
10981         if ( index < parent->GetChildCount() )
10982         {
10983             // Next sibling
10984             property = parent->Item(index);
10985         }
10986         else
10987         {
10988             // Next sibling of parent
10989             if ( parent == m_baseParent )
10990             {
10991                 m_property = NULL;
10992             }
10993             else
10994             {
10995                 m_property = parent;
10996                 Next(false);
10997             }
10998             return;
10999         }
11000     }
11001 
11002     m_property = property;
11003 
11004     // If property does not match our criteria, skip it
11005     if ( property->GetFlags() & m_itemExMask )
11006         Next();
11007 }
11008 
11009 // -----------------------------------------------------------------------
11010 // wxPGVIterator_State
11011 // -----------------------------------------------------------------------
11012 
11013 // Default returned by wxPropertyGridInterface::GetVIterator().
11014 class wxPGVIteratorBase_State : public wxPGVIteratorBase
11015 {
11016 public:
wxPGVIteratorBase_State(wxPropertyGridState * state,int flags)11017     wxPGVIteratorBase_State( wxPropertyGridState* state, int flags )
11018     {
11019         m_it.Init( state, flags );
11020     }
~wxPGVIteratorBase_State()11021     ~wxPGVIteratorBase_State() override { }
Next()11022     void Next() override { m_it.Next(); }
11023 };
11024 
GetVIterator(int flags) const11025 wxPGVIterator wxPropertyGridInterface::GetVIterator( int flags ) const
11026 {
11027     return wxPGVIterator( new wxPGVIteratorBase_State( m_pState, flags ) );
11028 }
11029 
11030 // EscapeDelimiters() changes ";" into "\;" and "|" into "\|"
11031 // in the input string.  This is an internal functions which is
11032 // used for saving states
11033 // NB: Similar function exists in aui/framemanager.cpp
EscapeDelimiters(const wxString & s)11034 static wxString EscapeDelimiters(const wxString& s)
11035 {
11036     wxString result;
11037     result.Alloc(s.length());
11038     const wxChar* ch = s.c_str();
11039     while (*ch)
11040     {
11041         if (*ch == wxT(';') || *ch == wxT('|') || *ch == wxT(','))
11042             result += wxT('\\');
11043         result += *ch;
11044         ++ch;
11045     }
11046     return result;
11047 }
11048 
SaveEditableState(int includedStates) const11049 wxString wxPropertyGridInterface::SaveEditableState( int includedStates ) const
11050 {
11051     wxString result;
11052 
11053     //
11054     // Save state on page basis
11055     size_t pageIndex = 0;
11056     wxPropertyGridState* pageStateIdx = GetPageState(pageIndex);
11057     wxArrayPtrVoid pageStates;
11058     while ( pageStateIdx )
11059     {
11060         pageStates.Add(pageStateIdx);
11061         pageIndex += 1;
11062         pageStateIdx = GetPageState(pageIndex);
11063     }
11064 
11065     for ( pageIndex=0; pageIndex < pageStates.size(); pageIndex++ )
11066     {
11067         wxPropertyGridState* pageState = (wxPropertyGridState*) pageStates[pageIndex];
11068 
11069         if ( includedStates & SelectionState )
11070         {
11071             wxString sel;
11072             if ( pageState->GetSelection() )
11073                 sel = pageState->GetSelection()->GetName();
11074             result += wxT("selection=");
11075             result += EscapeDelimiters(sel);
11076             result += wxT(";");
11077         }
11078         if ( includedStates & ExpandedState )
11079         {
11080             wxArrayPGProperty ptrs;
11081             wxPropertyGridConstIterator it =
11082                 wxPropertyGridConstIterator( pageState,
11083                                              wxPG_ITERATE_ALL_PARENTS_RECURSIVELY|wxPG_ITERATE_HIDDEN,
11084                                              wxNullProperty );
11085 
11086             result += wxT("expanded=");
11087 
11088             for ( ;
11089                   !it.AtEnd();
11090                   it.Next() )
11091             {
11092                 const wxPGProperty* p = it.GetProperty();
11093 
11094                 if ( !p->HasFlag(wxPG_PROP_COLLAPSED) )
11095                 {
11096                     result += EscapeDelimiters(p->GetName());
11097                     result += wxT(",");
11098                 }
11099             }
11100 
11101             if ( result.Last() == wxT(',') )
11102                 result.RemoveLast();
11103 
11104             result += wxT(";");
11105         }
11106         if ( includedStates & ScrollPosState )
11107         {
11108             int x, y;
11109             GetPropertyGrid()->GetViewStart(&x,&y);
11110             result += wxString::Format(wxT("scrollpos=%i,%i;"), x, y);
11111         }
11112         if ( includedStates & SplitterPosState )
11113         {
11114             result += wxT("splitterpos=");
11115 
11116             for ( size_t i=0; i<pageState->GetColumnCount(); i++ )
11117                 result += wxString::Format(wxT("%i,"), pageState->DoGetSplitterPosition(i));
11118 
11119             result.RemoveLast();  // Remove last comma
11120             result += wxT(";");
11121         }
11122         if ( includedStates & PageState )
11123         {
11124             result += wxT("ispageselected=");
11125 
11126             if ( GetPageState(-1) == pageState )
11127                 result += wxT("1;");
11128             else
11129                 result += wxT("0;");
11130         }
11131         if ( includedStates & DescBoxState )
11132         {
11133             wxVariant v = GetEditableStateItem(wxT("descboxheight"));
11134             if ( !v.IsNull() )
11135                 result += wxString::Format(wxT("descboxheight=%i;"), (int)v.GetLong());
11136         }
11137         result.RemoveLast();  // Remove last semicolon
11138         result += wxT("|");
11139     }
11140 
11141     // Remove last '|'
11142     if ( result.length() )
11143         result.RemoveLast();
11144 
11145     return result;
11146 }
11147 
RestoreEditableState(const wxString & src,int restoreStates)11148 bool wxPropertyGridInterface::RestoreEditableState( const wxString& src, int restoreStates )
11149 {
11150     wxPropertyGrid* pg = GetPropertyGrid();
11151     wxPGProperty* newSelection = NULL;
11152     size_t pageIndex;
11153     long vx = -1;
11154     long vy = -1;
11155     long selectedPage = -1;
11156     bool pgSelectionSet = false;
11157     bool res = true;
11158 
11159     pg->Freeze();
11160     wxArrayString pageStrings = ::wxSplit(src, wxT('|'), wxT('\\'));
11161 
11162     for ( pageIndex=0; pageIndex<pageStrings.size(); pageIndex++ )
11163     {
11164         wxPropertyGridState* pageState = GetPageState(pageIndex);
11165         if ( !pageState )
11166             break;
11167 
11168         wxArrayString kvpairStrings = ::wxSplit(pageStrings[pageIndex], wxT(';'), wxT('\\'));
11169 
11170         for ( size_t i=0; i<kvpairStrings.size(); i++ )
11171         {
11172             const wxString& kvs = kvpairStrings[i];
11173             int eq_pos = kvs.Find(wxT('='));
11174             if ( eq_pos != wxNOT_FOUND )
11175             {
11176                 wxString key = kvs.substr(0, eq_pos);
11177                 wxString value = kvs.substr(eq_pos+1);
11178 
11179                 // Further split value by commas
11180                 wxArrayString values = ::wxSplit(value, wxT(','), wxT('\\'));
11181 
11182                 if ( key == wxT("expanded") )
11183                 {
11184                     if ( restoreStates & ExpandedState )
11185                     {
11186                         wxPropertyGridIterator it =
11187                             wxPropertyGridIterator( pageState,
11188                                                     wxPG_ITERATE_ALL,
11189                                                     wxNullProperty );
11190 
11191                         // First collapse all
11192                         for ( ; !it.AtEnd(); it.Next() )
11193                         {
11194                             wxPGProperty* p = it.GetProperty();
11195                             pageState->DoCollapse(p);
11196                         }
11197 
11198                         // Then expand those which names are in values
11199                         for ( size_t n=0; n<values.size(); n++ )
11200                         {
11201                             const wxString& name = values[n];
11202                             wxPGProperty* prop = GetPropertyByName(name);
11203                             if ( prop )
11204                                 pageState->DoExpand(prop);
11205                         }
11206                     }
11207                 }
11208                 else if ( key == wxT("scrollpos") )
11209                 {
11210                     if ( restoreStates & ScrollPosState )
11211                     {
11212                         if ( values.size() == 2 )
11213                         {
11214                             values[0].ToLong(&vx);
11215                             values[1].ToLong(&vy);
11216                         }
11217                         else
11218                         {
11219                             res = false;
11220                         }
11221                     }
11222                 }
11223                 else if ( key == wxT("splitterpos") )
11224                 {
11225                     if ( restoreStates & SplitterPosState )
11226                     {
11227                         for ( size_t n=1; n<values.size(); n++ )
11228                         {
11229                             long pos = 0;
11230                             values[n].ToLong(&pos);
11231                             if ( pos > 0 )
11232                                 pageState->DoSetSplitterPosition(pos, n);
11233                         }
11234                     }
11235                 }
11236                 else if ( key == wxT("selection") )
11237                 {
11238                     if ( restoreStates & SelectionState )
11239                     {
11240                         if ( values.size() > 0 )
11241                         {
11242                             if ( pageState->IsDisplayed() )
11243                             {
11244                                 if ( values[0].length() )
11245                                     newSelection = GetPropertyByName(value);
11246                                 pgSelectionSet = true;
11247                             }
11248                             else
11249                             {
11250                                 if ( values[0].length() )
11251                                     pageState->DoSetSelection(GetPropertyByName(value));
11252                                 else
11253                                     pageState->DoClearSelection();
11254                             }
11255                         }
11256                     }
11257                 }
11258                 else if ( key == wxT("ispageselected") )
11259                 {
11260                     if ( restoreStates & PageState )
11261                     {
11262                         long pageSelStatus;
11263                         if ( values.size() == 1 && values[0].ToLong(&pageSelStatus) )
11264                         {
11265                             if ( pageSelStatus )
11266                                 selectedPage = pageIndex;
11267                         }
11268                         else
11269                         {
11270                             res = false;
11271                         }
11272                     }
11273                 }
11274                 else if ( key == wxT("descboxheight") )
11275                 {
11276                     if ( restoreStates & DescBoxState )
11277                     {
11278                         long descBoxHeight;
11279                         if ( values.size() == 1 && values[0].ToLong(&descBoxHeight) )
11280                         {
11281                             SetEditableStateItem(wxT("descboxheight"), descBoxHeight);
11282                         }
11283                         else
11284                         {
11285                             res = false;
11286                         }
11287                     }
11288                 }
11289                 else
11290                 {
11291                     res = false;
11292                 }
11293             }
11294         }
11295     }
11296 
11297     //
11298     // Force recalculation of virtual heights of all pages
11299     // (may be needed on unclean source string).
11300     pageIndex = 0;
11301     wxPropertyGridState* pageState = GetPageState(pageIndex);
11302     while ( pageState )
11303     {
11304         pageState->VirtualHeightChanged();
11305         pageIndex += 1;
11306         pageState = GetPageState(pageIndex);
11307     }
11308 
11309     pg->Thaw();
11310 
11311     //
11312     // Selection of visible grid page must be set after Thaw() call
11313     if ( pgSelectionSet )
11314     {
11315         if ( newSelection )
11316             pg->SelectProperty(newSelection);
11317         else
11318             pg->ClearSelection();
11319     }
11320 
11321     if ( selectedPage != -1 )
11322     {
11323         DoSelectPage(selectedPage);
11324     }
11325 
11326     if ( vx >= 0 )
11327     {
11328         pg->Scroll(vx, vy);
11329     }
11330 
11331     return res;
11332 }
11333 
11334 // -----------------------------------------------------------------------
11335 // wxPropertyGridState
11336 // -----------------------------------------------------------------------
11337 
11338 // -----------------------------------------------------------------------
11339 // wxPropertyGridState item iteration methods
11340 // -----------------------------------------------------------------------
11341 
11342 #if wxPG_COMPATIBILITY_1_2_0
11343 
11344 // Skips categories and sub-properties (unless in wxParentProperty).
GetNextProperty(wxPGProperty * p)11345 wxPGProperty* wxPropertyGridState::GetNextProperty( wxPGProperty* p )
11346 {
11347     wxPropertyGridIterator it( (wxPropertyGridState*)this, wxPG_ITERATE_DEFAULT, p );
11348 
11349     it.Next();
11350     while ( !it.AtEnd() )
11351         it.Next();
11352 
11353     return it.GetProperty();
11354 }
11355 
11356 // -----------------------------------------------------------------------
11357 
GetNextSiblingProperty(wxPGProperty * p)11358 wxPGProperty* wxPropertyGridState::GetNextSiblingProperty( wxPGProperty* p )
11359 {
11360     wxPGProperty* parent = p->m_parent;
11361     size_t next_ind = p->m_arrIndex + 1;
11362     if ( next_ind >= parent->GetCount() ) return (wxPGProperty*)NULL;
11363     return parent->Item(next_ind);
11364 }
11365 
11366 // -----------------------------------------------------------------------
11367 
GetPrevSiblingProperty(wxPGProperty * p)11368 wxPGProperty* wxPropertyGridState::GetPrevSiblingProperty( wxPGProperty* p )
11369 {
11370     size_t ind = p->m_arrIndex;
11371     if ( ind < 1 ) return (wxPGProperty*)NULL;
11372     return p->m_parent->Item(ind-1);
11373 }
11374 
11375 // -----------------------------------------------------------------------
11376 
11377 // Skips categories and sub-properties (unless in wxParentProperty).
GetPrevProperty(wxPGProperty * p)11378 wxPGProperty* wxPropertyGridState::GetPrevProperty( wxPGProperty* p )
11379 {
11380     wxPropertyGridIterator it( (wxPropertyGridState*)this, wxPG_ITERATE_DEFAULT, p );
11381 
11382     it.Prev();
11383     while ( !it.AtEnd() )
11384         it.Prev();
11385 
11386     return it.GetProperty();
11387 }
11388 
11389 // -----------------------------------------------------------------------
11390 
GetNextCategory(wxPGProperty * p) const11391 wxPGProperty* wxPropertyGridState::GetNextCategory( wxPGProperty* p ) const
11392 {
11393     wxPGProperty* current = (wxPGProperty*)p;
11394 
11395     wxCHECK_MSG( !IsInNonCatMode() || current->IsCategory(), (wxPGProperty*)NULL,
11396         wxT("GetNextCategory should not be called with non-category argument in non-categoric mode.") );
11397 
11398     wxPropertyGridConstIterator it( this, wxPG_ITERATE_CATEGORIES, p );
11399     if ( *it != p )
11400         return (wxPGProperty*) *it;
11401     it++;
11402     return (wxPGProperty*) *it;
11403 }
11404 
11405 #endif // #if wxPG_COMPATIBILITY_1_2_0
11406 
11407 // -----------------------------------------------------------------------
11408 
GetLastItem(int flags)11409 wxPGProperty* wxPropertyGridState::GetLastItem( int flags )
11410 {
11411     if ( !m_properties->GetCount() )
11412         return (wxPGProperty*) NULL;
11413 
11414     wxPG_ITERATOR_CREATE_MASKS(flags, int itemExMask, int parentExMask)
11415 
11416     // First, get last child of last parent
11417     wxPGProperty* pwc = (wxPGProperty*)m_properties->Last();
11418     while ( pwc->GetChildCount() &&
11419             wxPG_ITERATOR_PARENTEXMASK_TEST(pwc, parentExMask) )
11420         pwc = (wxPGProperty*) pwc->Last();
11421 
11422     // Then, if it doesn't fit our criteria, back up until we find something that does
11423     if ( pwc->GetFlags() & itemExMask )
11424     {
11425         wxPropertyGridIterator it( this, flags, pwc );
11426         for ( ; !it.AtEnd(); it.Prev() );
11427         pwc = (wxPGProperty*) it.GetProperty();
11428     }
11429 
11430     return pwc;
11431 }
11432 
GetPropertyCategory(const wxPGProperty * p) const11433 wxPropertyCategory* wxPropertyGridState::GetPropertyCategory( const wxPGProperty* p ) const
11434 {
11435     const wxPGProperty* parent = (const wxPGProperty*)p;
11436     const wxPGProperty* grandparent = (const wxPGProperty*)parent->GetParent();
11437     do
11438     {
11439         parent = grandparent;
11440         grandparent = (wxPGProperty*)parent->GetParent();
11441         if ( parent->IsCategory() && grandparent )
11442             return (wxPropertyCategory*)parent;
11443     } while ( grandparent );
11444 
11445     return (wxPropertyCategory*) NULL;
11446 }
11447 
11448 // -----------------------------------------------------------------------
11449 // wxPropertyGridState GetPropertyXXX methods
11450 // -----------------------------------------------------------------------
11451 
GetPropertyByLabel(const wxString & label,wxPGProperty * parent) const11452 wxPGProperty* wxPropertyGridState::GetPropertyByLabel( const wxString& label,
11453                                                        wxPGProperty* parent ) const
11454 {
11455 
11456     size_t i;
11457 
11458     if ( !parent ) parent = (wxPGProperty*) &m_regularArray;
11459 
11460     for ( i=0; i<parent->GetCount(); i++ )
11461     {
11462         wxPGProperty* p = parent->Item(i);
11463         if ( p->m_label == label )
11464             return p;
11465         // Check children recursively.
11466         if ( p->GetChildCount() )
11467         {
11468             p = GetPropertyByLabel(label,(wxPGProperty*)p);
11469             if ( p )
11470                 return p;
11471         }
11472     }
11473 
11474     return NULL;
11475 }
11476 
11477 // -----------------------------------------------------------------------
11478 
BaseGetPropertyByName(wxPGPropNameStr name) const11479 wxPGProperty* wxPropertyGridState::BaseGetPropertyByName( wxPGPropNameStr name ) const
11480 {
11481     wxPGHashMapS2P::const_iterator it;
11482     it = m_dictName.find(name);
11483     if ( it != m_dictName.end() )
11484         return (wxPGProperty*) it->second;
11485     return (wxPGProperty*) NULL;
11486 }
11487 
11488 // -----------------------------------------------------------------------
11489 // wxPropertyGridState global operations
11490 // -----------------------------------------------------------------------
11491 
EnableCategories(bool enable)11492 bool wxPropertyGridState::EnableCategories( bool enable )
11493 {
11494     //
11495     // NB: We can't use wxPropertyGridIterator in this
11496     //     function, since it depends on m_arrIndexes,
11497     //     which, among other things, is being fixed here.
11498     //
11499     ITEM_ITERATION_VARIABLES
11500 
11501     if ( enable )
11502     {
11503         //
11504         // Enable categories
11505         //
11506 
11507         if ( !IsInNonCatMode() )
11508             return false;
11509 
11510         m_properties = &m_regularArray;
11511 
11512         // fix parents, indexes, and depths
11513         ITEM_ITERATION_INIT_FROM_THE_TOP
11514 
11515         ITEM_ITERATION_LOOP_BEGIN
11516 
11517             p->m_arrIndex = i;
11518 
11519             p->m_parent = parent;
11520 
11521             // If parent was category, and this is not,
11522             // then the depth stays the same.
11523             if ( parent->IsCategory() &&
11524                  !p->IsCategory() )
11525                 p->m_depth = parent->m_depth;
11526             else
11527                 p->m_depth = parent->m_depth + 1;
11528 
11529         ITEM_ITERATION_LOOP_END
11530 
11531     }
11532     else
11533     {
11534         //
11535         // Disable categories
11536         //
11537 
11538         if ( IsInNonCatMode() )
11539             return false;
11540 
11541         // Create array, if necessary.
11542         if ( !m_abcArray )
11543             InitNonCatMode();
11544 
11545         m_properties = m_abcArray;
11546 
11547         // fix parents, indexes, and depths
11548         ITEM_ITERATION_INIT_FROM_THE_TOP
11549 
11550         ITEM_ITERATION_LOOP_BEGIN
11551 
11552             p->m_arrIndex = i;
11553 
11554             p->m_parent = parent;
11555 
11556             p->m_depth = parent->m_depth + 1;
11557 
11558         ITEM_ITERATION_LOOP_END
11559     }
11560 
11561     VirtualHeightChanged();
11562 
11563     if ( m_pPropGrid->GetState() == this )
11564         m_pPropGrid->RecalculateVirtualSize();
11565 
11566     return true;
11567 }
11568 
11569 // -----------------------------------------------------------------------
11570 
wxPG_SortFunc_ByFunction(void ** p1,void ** p2)11571 static int wxPG_SortFunc_ByFunction(void **p1, void **p2)
11572 {
11573     wxPGProperty *pp1 = *((wxPGProperty**)p1);
11574     wxPGProperty *pp2 = *((wxPGProperty**)p2);
11575     wxPropertyGrid* pg = pp1->GetGrid();
11576     wxPGSortCallback sortFunction = pg->GetSortFunction();
11577     return sortFunction(pg, pp1, pp2);
11578 }
11579 
wxPG_SortFunc_ByLabel(void ** p1,void ** p2)11580 static int wxPG_SortFunc_ByLabel(void **p1, void **p2)
11581 {
11582     wxPGProperty *pp1 = *((wxPGProperty**)p1);
11583     wxPGProperty *pp2 = *((wxPGProperty**)p2);
11584     return pp1->GetLabel().CmpNoCase( pp2->GetLabel() );
11585 }
11586 
Sort(wxPGProperty * p)11587 void wxPropertyGridState::Sort( wxPGProperty* p )
11588 {
11589     if ( !p )
11590         p = (wxPGProperty*)m_properties;
11591 
11592     if ( !p->GetChildCount() )
11593         return;
11594 
11595     wxPGProperty* pwc = (wxPGProperty*)p;
11596 
11597     // Can only sort items with children
11598     if ( pwc->m_children.GetCount() < 1 )
11599         return;
11600 
11601     if ( GetGrid()->GetSortFunction() )
11602         pwc->m_children.Sort( wxPG_SortFunc_ByFunction );
11603     else
11604         pwc->m_children.Sort( wxPG_SortFunc_ByLabel );
11605 
11606     // Fix indexes
11607     pwc->FixIndexesOfChildren();
11608 
11609 }
11610 
11611 // -----------------------------------------------------------------------
11612 
Sort()11613 void wxPropertyGridState::Sort()
11614 {
11615     Sort( m_properties );
11616 
11617     // Sort categories as well
11618     if ( !IsInNonCatMode() )
11619     {
11620         size_t i;
11621         for ( i=0;i<m_properties->GetCount();i++)
11622         {
11623             wxPGProperty* p = m_properties->Item(i);
11624             if ( p->IsCategory() )
11625                 Sort( p );
11626         }
11627     }
11628 }
11629 
11630 // -----------------------------------------------------------------------
11631 // wxPropertyGridState splitter, column and hittest functions
11632 // -----------------------------------------------------------------------
11633 
DoGetItemAtY(int y) const11634 wxPGProperty* wxPropertyGridState::DoGetItemAtY( int y ) const
11635 {
11636     // Outside?
11637     if ( y < 0 )
11638         return (wxPGProperty*) NULL;
11639 
11640     unsigned int a = 0;
11641     return m_properties->GetItemAtY(y, GetGrid()->m_lineHeight, &a);
11642 }
11643 
11644 // -----------------------------------------------------------------------
11645 
HitTest(const wxPoint & pt) const11646 wxPropertyGridHitTestResult wxPropertyGridState::HitTest( const wxPoint&pt ) const
11647 {
11648     wxPropertyGridHitTestResult result;
11649     result.column = HitTestH( pt.x, &result.splitter, &result.splitterHitOffset );
11650     result.property = DoGetItemAtY( pt.y );
11651     return result;
11652 }
11653 
11654 // -----------------------------------------------------------------------
11655 
11656 // Used by SetSplitterLeft() and DotFitColumns()
GetColumnFitWidth(wxClientDC & dc,wxPGProperty * pwc,unsigned int col,bool subProps) const11657 int wxPropertyGridState::GetColumnFitWidth(wxClientDC& dc,
11658                                            wxPGProperty* pwc,
11659                                            unsigned int col,
11660                                            bool subProps) const
11661 {
11662     wxPropertyGrid* pg = m_pPropGrid;
11663     size_t i;
11664     int maxW = 0;
11665     int w, h;
11666 
11667     for ( i=0; i<pwc->GetCount(); i++ )
11668     {
11669         wxPGProperty* p = pwc->Item(i);
11670         if ( !p->IsCategory() )
11671         {
11672             dc.GetTextExtent( p->GetColumnText(col), &w, &h );
11673             if ( col == 0 )
11674                 w += ( ((int)p->m_depth-1) * pg->m_subgroup_extramargin );
11675 
11676             // account for the bitmap
11677             if ( col == 1 )
11678                 w += p->GetImageOffset(pg->GetImageRect(p, -1).GetWidth());
11679 
11680             w += (wxPG_XBEFORETEXT*2);
11681 
11682             if ( w > maxW )
11683                 maxW = w;
11684         }
11685 
11686         if ( p->GetChildCount() &&
11687              ( subProps || p->IsCategory() ) )
11688         {
11689             w = GetColumnFitWidth( dc, p, col, subProps );
11690 
11691             if ( w > maxW )
11692                 maxW = w;
11693         }
11694     }
11695 
11696     return maxW;
11697 }
11698 
11699 /* C::B begin */
GetColumnFullWidth(wxClientDC & dc,wxPGProperty * p,unsigned int col)11700 int wxPropertyGridState::GetColumnFullWidth( wxClientDC &dc, wxPGProperty *p, unsigned int col )
11701 {
11702     if ( p->IsCategory() )
11703         return 0;
11704 
11705     long w, h;
11706     dc.GetTextExtent( p->GetColumnText(col), &w, &h );
11707     if ( col == 0 )
11708         w += (int)p->m_depth * m_pPropGrid->m_subgroup_extramargin;
11709 
11710     // account for the bitmap
11711     if ( col == 1 )
11712         w += p->GetImageOffset(m_pPropGrid->GetImageRect(p, -1).GetWidth());
11713 
11714     w += (wxPG_XBEFORETEXT*2);
11715     return w;
11716 }
11717 /* C::B end */
11718 
DoGetSplitterPosition(int splitterColumn) const11719 int wxPropertyGridState::DoGetSplitterPosition( int splitterColumn ) const
11720 {
11721     int n = GetGrid()->m_marginWidth;
11722     int i;
11723     for ( i=0; i<=splitterColumn; i++ )
11724         n += m_colWidths[i];
11725     return n;
11726 }
11727 
GetColumnMinWidth(int WXUNUSED (column)) const11728 int wxPropertyGridState::GetColumnMinWidth( int WXUNUSED(column) ) const
11729 {
11730     return wxPG_DRAG_MARGIN;
11731 }
11732 
PropagateColSizeDec(int column,int decrease,int dir)11733 void wxPropertyGridState::PropagateColSizeDec( int column, int decrease, int dir )
11734 {
11735     int origWidth = m_colWidths[column];
11736     m_colWidths[column] -= decrease;
11737     int min = GetColumnMinWidth(column);
11738     int more = 0;
11739     if ( m_colWidths[column] < min )
11740     {
11741         more = decrease - (origWidth - min);
11742         m_colWidths[column] = min;
11743     }
11744 
11745     //
11746     // FIXME: Causes erratic splitter changing, so as a workaround
11747     //        disabled if two or less columns.
11748 
11749     if ( m_colWidths.size() <= 2 )
11750         return;
11751 
11752     column += dir;
11753     if ( more && column < (int)m_colWidths.size() && column >= 0 )
11754         PropagateColSizeDec( column, more, dir );
11755 }
11756 
DoSetSplitterPosition(int newXPos,int splitterColumn,bool WXUNUSED (allPages),bool fromAutoCenter)11757 void wxPropertyGridState::DoSetSplitterPosition( int newXPos, int splitterColumn, bool WXUNUSED(allPages), bool fromAutoCenter )
11758 {
11759     wxPropertyGrid* pg = GetGrid();
11760 
11761     int adjust = newXPos - DoGetSplitterPosition(splitterColumn);
11762 
11763     if ( !pg->HasVirtualWidth() )
11764     {
11765         // No virtual width
11766         int otherColumn;
11767         if ( adjust > 0 )
11768         {
11769             otherColumn = splitterColumn + 1;
11770             if ( otherColumn == (int)m_colWidths.size() )
11771                 otherColumn = 0;
11772             m_colWidths[splitterColumn] += adjust;
11773             PropagateColSizeDec( otherColumn, adjust, 1 );
11774         }
11775         else
11776         {
11777             otherColumn = splitterColumn + 1;
11778             if ( otherColumn == (int)m_colWidths.size() )
11779                 otherColumn = 0;
11780             m_colWidths[otherColumn] -= adjust;
11781             PropagateColSizeDec( splitterColumn, -adjust, -1 );
11782         }
11783     }
11784     else
11785     {
11786         m_colWidths[splitterColumn] += adjust;
11787     }
11788 
11789     if ( splitterColumn == 0 )
11790         m_fSplitterX = (double) newXPos;
11791 
11792     if ( !fromAutoCenter )
11793     {
11794         // Don't allow initial splitter auto-positioning after this.
11795         if ( pg->GetState() == this )
11796             pg->SetInternalFlag(wxPG_FL_SPLITTER_PRE_SET);
11797 
11798         CheckColumnWidths();
11799     }
11800 }
11801 
11802 // Moves splitter so that all labels are visible, but just.
SetSplitterLeft(bool subProps)11803 void wxPropertyGridState::SetSplitterLeft( bool subProps )
11804 {
11805     wxPropertyGrid* pg = GetGrid();
11806     wxClientDC dc(pg);
11807     dc.SetFont(pg->GetFont());
11808 
11809     int maxW = GetColumnFitWidth(dc, m_properties, 0, subProps);
11810 
11811     if ( maxW > 0 )
11812     {
11813         maxW += pg->m_marginWidth;
11814         DoSetSplitterPosition( maxW );
11815     }
11816 
11817     pg->SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
11818 }
11819 
DoFitColumns(bool WXUNUSED (allowGridResize))11820 wxSize wxPropertyGridState::DoFitColumns( bool WXUNUSED(allowGridResize) )
11821 {
11822     wxPropertyGrid* pg = GetGrid();
11823     wxClientDC dc(pg);
11824     dc.SetFont(pg->GetFont());
11825 
11826     int marginWidth = pg->m_marginWidth;
11827     int accWid = marginWidth;
11828     int maxColWidth = 500;
11829 
11830     for ( unsigned int col=0; col < GetColumnCount(); col++ )
11831     {
11832         int fitWid = GetColumnFitWidth(dc, m_properties, col, true);
11833         int colMinWidth = GetColumnMinWidth(col);
11834         if ( fitWid < colMinWidth )
11835             fitWid = colMinWidth;
11836         else if ( fitWid > maxColWidth )
11837             fitWid = maxColWidth;
11838 
11839         m_colWidths[col] = fitWid;
11840 
11841         accWid += fitWid;
11842     }
11843 
11844     // Expand last one to fill the width
11845     int remaining = m_width - accWid;
11846     m_colWidths[GetColumnCount()-1] += remaining;
11847 
11848     pg->SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
11849 
11850     int firstSplitterX = marginWidth + m_colWidths[0];
11851     m_fSplitterX = (double) firstSplitterX;
11852 
11853     // Don't allow initial splitter auto-positioning after this.
11854     if ( pg->GetState() == this )
11855     {
11856         pg->SetSplitterPosition(firstSplitterX, false);
11857         pg->Refresh();
11858     }
11859 
11860     int x, y;
11861     pg->GetVirtualSize(&x, &y);
11862 
11863     return wxSize(accWid, y);
11864 }
11865 
CheckColumnWidths(int widthChange)11866 void wxPropertyGridState::CheckColumnWidths( int widthChange )
11867 {
11868     if ( m_width == 0 )
11869         return;
11870 
11871     wxPropertyGrid* pg = GetGrid();
11872 
11873 #ifdef __WXDEBUG__
11874     const bool debug = false;
11875 #endif
11876 
11877     unsigned int i;
11878     unsigned int lastColumn = m_colWidths.size() - 1;
11879     int width = m_width;
11880     int clientWidth = pg->GetClientSize().x;
11881 
11882     //
11883     // Column to reduce, if needed. Take last one that exceeds minimum width.
11884     int reduceCol = -1;
11885 
11886 #ifdef __WXDEBUG__
11887     if ( debug )
11888         wxLogDebug(wxT("ColumnWidthCheck (virtualWidth: %i, clientWidth: %i)"), width, clientWidth);
11889 #endif
11890 
11891     //
11892     // Check min sizes
11893     for ( i=0; i<m_colWidths.size(); i++ )
11894     {
11895         int min = GetColumnMinWidth(i);
11896         if ( m_colWidths[i] <= min )
11897         {
11898             m_colWidths[i] = min;
11899         }
11900         else
11901         {
11902             // Always reduce the last column that is larger than minimum size
11903             // (looks nicer, even with auto-centering enabled).
11904             reduceCol = i;
11905         }
11906     }
11907 
11908     int colsWidth = pg->m_marginWidth;
11909     for ( i=0; i<m_colWidths.size(); i++ )
11910         colsWidth += m_colWidths[i];
11911 
11912 #ifdef __WXDEBUG__
11913     if ( debug )
11914         wxLogDebug(wxT("  HasVirtualWidth: %i  colsWidth: %i"),(int)pg->HasVirtualWidth(),colsWidth);
11915 #endif
11916 
11917     // Then mode-based requirement
11918     if ( !pg->HasVirtualWidth() )
11919     {
11920         int widthHigher = width - colsWidth;
11921 
11922         // Adapt colsWidth to width
11923         if ( colsWidth < width )
11924         {
11925             // Increase column
11926         #ifdef __WXDEBUG__
11927         if ( debug )
11928             wxLogDebug(wxT("  Adjust last column to %i"), m_colWidths[lastColumn] + widthHigher);
11929         #endif
11930             m_colWidths[lastColumn] = m_colWidths[lastColumn] + widthHigher;
11931         }
11932         else if ( colsWidth > width )
11933         {
11934             // Reduce column
11935             if ( reduceCol != -1 )
11936             {
11937             #ifdef __WXDEBUG__
11938                 if ( debug )
11939                     wxLogDebug(wxT("  Reduce column %i (by %i)"), reduceCol, -widthHigher);
11940             #endif
11941                 // Reduce widest column, and recheck
11942                 m_colWidths[reduceCol] = m_colWidths[reduceCol] + widthHigher;
11943                 CheckColumnWidths();
11944             }
11945         }
11946     }
11947     else
11948     {
11949         // Only check colsWidth against clientWidth
11950         if ( colsWidth < clientWidth )
11951         {
11952             m_colWidths[lastColumn] = m_colWidths[lastColumn] + (clientWidth-colsWidth);
11953         }
11954 
11955         m_width = colsWidth;
11956 
11957         // If width changed, recalculate virtual size
11958         if ( pg->GetState() == this )
11959             pg->RecalculateVirtualSize();
11960     }
11961 
11962 #ifdef __WXDEBUG__
11963     if ( debug )
11964         for ( i=0; i<m_colWidths.size(); i++ )
11965             wxLogDebug(wxT("col%i: %i"),i,m_colWidths[i]);
11966 #endif
11967 
11968     // Auto center splitter
11969     if ( !(pg->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER) )
11970     {
11971         if ( m_colWidths.size() == 2 &&
11972              m_columnProportions[0] == m_columnProportions[1] )
11973         {
11974             //
11975             // When we have two columns of equal proportion, then use this
11976             // code. It will look nicer when the scrollbar visibility is
11977             // toggled on and off.
11978             //
11979             // TODO: Adapt this to generic recenter code.
11980             //
11981             float centerX = (float)(pg->m_width/2);
11982             float splitterX;
11983 
11984             if ( m_fSplitterX < 0.0 )
11985             {
11986             #ifdef __WXDEBUG__
11987                 if ( debug )
11988                     wxLogDebug(wxT("  auto-center splitter reset"));
11989             #endif
11990                 splitterX = centerX;
11991             }
11992             else if ( widthChange )
11993             {
11994             #ifdef __WXDEBUG__
11995                 if ( debug )
11996                     wxLogDebug(wxT("  auto-center with widthChange=%i"),
11997                                widthChange);
11998             #endif
11999                 //float centerX = float(pg->GetSize().x) * 0.5;
12000 
12001                 // Recenter?
12002                 splitterX = m_fSplitterX + (float(widthChange) * 0.5);
12003                 float deviation = fabs(centerX - splitterX);
12004 
12005                 // If deviating from center, adjust towards it
12006                 if ( deviation > 20.0 )
12007                 {
12008                     if ( splitterX > centerX)
12009                         splitterX -= 2;
12010                     else
12011                         splitterX += 2;
12012                 }
12013             }
12014             else
12015             {
12016                 // No width change, just keep sure we keep splitter position intact
12017                 splitterX = m_fSplitterX;
12018                 float deviation = fabs(centerX - splitterX);
12019             #ifdef __WXDEBUG__
12020                 if ( debug )
12021                     wxLogDebug(wxT("  auto-center with deviation=%.2f"),
12022                                deviation);
12023             #endif
12024                 if ( deviation > 50.0 )
12025                 {
12026                     splitterX = centerX;
12027                 }
12028             }
12029 
12030             DoSetSplitterPosition((int)splitterX, 0, false, true);
12031 
12032             m_fSplitterX = splitterX; // needed to retain accuracy
12033         }
12034         else
12035         {
12036             //
12037             // Generic re-center code
12038             //
12039             ResetColumnSizes(true);
12040         }
12041     }
12042 }
12043 
ResetColumnSizes(bool fromAutoCenter)12044 void wxPropertyGridState::ResetColumnSizes( bool fromAutoCenter )
12045 {
12046     unsigned int i;
12047 
12048     // Calculate sum of proportions
12049     int psum = 0;
12050     for ( i=0; i<m_colWidths.size(); i++ )
12051         psum += m_columnProportions[i];
12052     int puwid = (m_pPropGrid->m_width*256) / psum;
12053     int cpos = 0;
12054 
12055     // Apply proportions
12056     for ( i=0; i<(m_colWidths.size() - 1); i++ )
12057     {
12058         int cwid = (puwid*m_columnProportions[i]) / 256;
12059         cpos += cwid;
12060         DoSetSplitterPosition(cpos, i, false, fromAutoCenter);
12061     }
12062 }
12063 
SetColumnCount(int colCount)12064 void wxPropertyGridState::SetColumnCount( int colCount )
12065 {
12066     wxASSERT( colCount >= 2 );
12067     m_colWidths.SetCount( colCount, wxPG_DRAG_MARGIN );
12068     m_columnProportions.SetCount( colCount, 1 );
12069     if ( m_colWidths.size() > (unsigned int)colCount )
12070         m_colWidths.RemoveAt( m_colWidths.size()-1,
12071                               m_colWidths.size() - colCount );
12072 
12073     if ( m_pPropGrid->GetState() == this )
12074         m_pPropGrid->RecalculateVirtualSize();
12075     else
12076         CheckColumnWidths();
12077 }
12078 
DoSetColumnProportion(unsigned int column,int proportion)12079 void wxPropertyGridState::DoSetColumnProportion( unsigned int column,
12080                                                  int proportion )
12081 {
12082     wxASSERT_MSG( proportion >= 1,
12083                   wxT("Column proportion must 1 or higher") );
12084 
12085     if ( proportion < 1 )
12086         proportion = 1;
12087 
12088     while ( m_columnProportions.size() <= column )
12089         m_columnProportions.push_back(1);
12090 
12091     m_columnProportions[column] = proportion;
12092 }
12093 
12094 // Returns column index, -1 for margin
HitTestH(int x,int * pSplitterHit,int * pSplitterHitOffset) const12095 int wxPropertyGridState::HitTestH( int x, int* pSplitterHit, int* pSplitterHitOffset ) const
12096 {
12097     int cx = GetGrid()->m_marginWidth;
12098     int col = -1;
12099     int prevSplitter = -1;
12100 
12101     while ( x > cx )
12102     {
12103         col++;
12104         if ( col >= (int)m_colWidths.size() )
12105         {
12106             *pSplitterHit = -1;
12107             return col;
12108         }
12109         prevSplitter = cx;
12110         cx += m_colWidths[col];
12111     }
12112 
12113     // Near prev. splitter
12114     if ( col >= 1 )
12115     {
12116         int diff = x - prevSplitter;
12117         if ( abs(diff) < wxPG_SPLITTERX_DETECTMARGIN1 )
12118         {
12119             *pSplitterHit = col - 1;
12120             *pSplitterHitOffset = diff;
12121             return col;
12122         }
12123     }
12124 
12125     // Near next splitter
12126     int nextSplitter = cx;
12127     if ( col < (int)(m_colWidths.size()-1) )
12128     {
12129         int diff = x - nextSplitter;
12130         if ( abs(diff) < wxPG_SPLITTERX_DETECTMARGIN1 )
12131         {
12132             *pSplitterHit = col;
12133             *pSplitterHitOffset = diff;
12134             return col;
12135         }
12136     }
12137 
12138     *pSplitterHit = -1;
12139     return col;
12140 }
12141 
ArePropertiesAdjacent(wxPGProperty * prop1,wxPGProperty * prop2,int iterFlags) const12142 bool wxPropertyGridState::ArePropertiesAdjacent( wxPGProperty* prop1,
12143                                                  wxPGProperty* prop2,
12144                                                  int iterFlags ) const
12145 {
12146     const wxPGProperty* ap1 =
12147         wxPropertyGridConstIterator::OneStep(this,
12148                                              iterFlags,
12149                                              prop1,
12150                                              1);
12151     if ( ap1 && ap1 == prop2 )
12152         return true;
12153 
12154     const wxPGProperty* ap2 =
12155         wxPropertyGridConstIterator::OneStep(this,
12156                                              iterFlags,
12157                                              prop1,
12158                                              -1);
12159     if ( ap2 && ap2 == prop2 )
12160         return true;
12161 
12162     return false;
12163 }
12164 
SetColumnProportion(unsigned int column,int proportion)12165 bool wxPropertyGridInterface::SetColumnProportion( unsigned int column,
12166                                                    int proportion )
12167 {
12168     wxCHECK(m_pState, false);
12169     wxPropertyGrid* pg = m_pState->GetGrid();
12170     wxCHECK(pg, false);
12171     wxCHECK(pg->HasFlag(wxPG_SPLITTER_AUTO_CENTER), false);
12172     m_pState->DoSetColumnProportion(column, proportion);
12173     return true;
12174 }
12175 
12176 // -----------------------------------------------------------------------
12177 // wxPropertyGridState property value setting and getting
12178 // -----------------------------------------------------------------------
12179 
DoSetPropertyValueString(wxPGProperty * p,const wxString & value)12180 bool wxPropertyGridState::DoSetPropertyValueString( wxPGProperty* p, const wxString& value )
12181 {
12182     if ( p )
12183     {
12184         int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE|wxPG_PROGRAMMATIC_VALUE;
12185 
12186         wxVariant variant = p->GetValueRef();
12187         bool res;
12188 
12189         if ( p->GetMaxLength() <= 0 )
12190             res = p->ActualStringToValue( variant, value, flags );
12191         else
12192             res = p->ActualStringToValue( variant, value.Mid(0,p->GetMaxLength()), flags );
12193 
12194         if ( res )
12195         {
12196             p->SetValue(variant);
12197         }
12198 
12199         return true;
12200     }
12201     return false;
12202 }
12203 
12204 // -----------------------------------------------------------------------
12205 
DoSetPropertyValue(wxPGProperty * p,wxVariant & value)12206 bool wxPropertyGridState::DoSetPropertyValue( wxPGProperty* p, wxVariant& value )
12207 {
12208     if ( p )
12209     {
12210         p->SetValue(value);
12211         return true;
12212     }
12213     return false;
12214 }
12215 
12216 // -----------------------------------------------------------------------
12217 
DoSetPropertyValueWxObjectPtr(wxPGProperty * p,wxObject * value)12218 bool wxPropertyGridState::DoSetPropertyValueWxObjectPtr( wxPGProperty* p, wxObject* value )
12219 {
12220     if ( p )
12221     {
12222         // TODO: Add proper testing
12223         //if ( wxStrcmp( p->GetValue().GetType(),
12224         //               value->GetClassInfo()->GetClassName()
12225         //              ) == 0
12226         //   )
12227         {
12228             // wnd_primary has to be given so the control can be updated as well.
12229             wxVariant v(value);
12230             DoSetPropertyValue(p, v);
12231             return true;
12232         }
12233         //wxPGTypeOperationFailed ( p, wxT("wxObject"), wxT("Set") );
12234     }
12235     return false;
12236 }
12237 
12238 // -----------------------------------------------------------------------
12239 // wxPropertyGridState property operations
12240 // -----------------------------------------------------------------------
12241 
DoIsPropertySelected(wxPGProperty * prop) const12242 bool wxPropertyGridState::DoIsPropertySelected( wxPGProperty* prop ) const
12243 {
12244     const wxArrayPGProperty& selection = m_selection;
12245 
12246     for ( unsigned int i=0; i<selection.size(); i++ )
12247     {
12248         if ( selection[i] == prop )
12249             return true;
12250     }
12251 
12252     return false;
12253 }
12254 
12255 // -----------------------------------------------------------------------
12256 
DoRemoveFromSelection(wxPGProperty * prop)12257 void wxPropertyGridState::DoRemoveFromSelection( wxPGProperty* prop )
12258 {
12259     for ( unsigned int i=0; i<m_selection.size(); i++ )
12260     {
12261         if ( m_selection[i] == prop )
12262         {
12263             wxPropertyGrid* pg = m_pPropGrid;
12264             if ( i == 0 && pg ->GetState() == this )
12265             {
12266                 // If first item (ie. one with the active editor) was
12267                 // deselected, then we need to take some extra measures.
12268                 wxArrayPGProperty sel = m_selection;
12269                 sel.erase( sel.begin() + i );
12270 
12271                 wxPGProperty* newFirst;
12272                 if ( sel.size() )
12273                     newFirst = sel[0];
12274                 else
12275                     newFirst = NULL;
12276 
12277                 pg->DoSelectProperty(newFirst,
12278                                      wxPG_SEL_DONT_SEND_EVENT);
12279 
12280                 m_selection = sel;
12281 
12282                 pg->Refresh();
12283             }
12284             else
12285             {
12286                 m_selection.erase( m_selection.begin() + i );
12287             }
12288             return;
12289         }
12290     }
12291 }
12292 
12293 // -----------------------------------------------------------------------
12294 
ClearModifiedStatus(wxPGProperty * p)12295 void wxPropertyGridState::ClearModifiedStatus( wxPGProperty* p )
12296 {
12297     if ( p->m_flags & wxPG_PROP_MODIFIED )
12298     {
12299         p->m_flags &= ~(wxPG_PROP_MODIFIED);
12300 
12301         if ( m_pPropGrid->GetState() == this )
12302         {
12303             // Clear active editor bold
12304             if ( p == GetSelection() )
12305                 m_pPropGrid->RefreshEditor();
12306 
12307             m_pPropGrid->DrawItem( p );
12308         }
12309     }
12310 
12311     size_t i;
12312     for ( i = 0; i < p->GetChildCount(); i++ )
12313         ClearModifiedStatus( p->Item(i) );
12314 }
12315 
12316 // -----------------------------------------------------------------------
12317 
DoCollapse(wxPGProperty * p)12318 bool wxPropertyGridState::DoCollapse( wxPGProperty* p )
12319 {
12320     wxCHECK_MSG( p, false, wxT("invalid property id") );
12321 
12322     wxPGProperty* pwc = (wxPGProperty*)p;
12323     if ( !pwc->GetChildCount() ) return false;
12324 
12325     if ( !pwc->IsExpanded() ) return false;
12326 
12327     pwc->SetExpanded(false);
12328 
12329     VirtualHeightChanged();
12330 
12331     return true;
12332 }
12333 
12334 // -----------------------------------------------------------------------
12335 
DoExpand(wxPGProperty * p)12336 bool wxPropertyGridState::DoExpand( wxPGProperty* p )
12337 {
12338     wxCHECK_MSG( p, false, wxT("invalid property id") );
12339 
12340     wxPGProperty* pwc = (wxPGProperty*)p;
12341     if ( !pwc->GetChildCount() ) return false;
12342 
12343     if ( pwc->IsExpanded() ) return false;
12344 
12345     pwc->SetExpanded(true);
12346 
12347     VirtualHeightChanged();
12348 
12349     return true;
12350 }
12351 
12352 // -----------------------------------------------------------------------
12353 
DoSelectProperty(wxPGProperty * p,unsigned int flags)12354 bool wxPropertyGridState::DoSelectProperty( wxPGProperty* p, unsigned int flags )
12355 {
12356     if ( this == m_pPropGrid->GetState() )
12357         return m_pPropGrid->DoSelectProperty( p, flags );
12358 
12359     DoSetSelection(p);
12360     return true;
12361 }
12362 
12363 // -----------------------------------------------------------------------
12364 
DoHideProperty(wxPGProperty * p,bool hide,int flags)12365 bool wxPropertyGridState::DoHideProperty( wxPGProperty* p, bool hide, int flags )
12366 {
12367     p->DoHide(hide, flags);
12368     VirtualHeightChanged();
12369 
12370     return true;
12371 }
12372 
12373 // -----------------------------------------------------------------------
12374 
DoEnableProperty(wxPGProperty * p,bool enable)12375 bool wxPropertyGridState::DoEnableProperty( wxPGProperty* p, bool enable )
12376 {
12377     if ( p )
12378     {
12379         if ( enable )
12380         {
12381             if ( !(p->m_flags & wxPG_PROP_DISABLED) )
12382                 return false;
12383 
12384             // Enabling
12385 
12386             p->m_flags &= ~(wxPG_PROP_DISABLED);
12387         }
12388         else
12389         {
12390             if ( p->m_flags & wxPG_PROP_DISABLED )
12391                 return false;
12392 
12393             // Disabling
12394 
12395             p->m_flags |= wxPG_PROP_DISABLED;
12396 
12397         }
12398 
12399         // Apply same to sub-properties as well
12400         unsigned int i;
12401         for ( i = 0; i < p->GetChildCount(); i++ )
12402             DoEnableProperty( p->Item(i), enable );
12403 
12404         return true;
12405     }
12406     return false;
12407 }
12408 
12409 // -----------------------------------------------------------------------
12410 // wxPropertyGridState wxVariant related routines
12411 // -----------------------------------------------------------------------
12412 
12413 // Returns list of wxVariant objects (non-categories and non-sub-properties only).
12414 // Never includes sub-properties (unless they are parented by wxParentProperty).
DoGetPropertyValues(const wxString & listname,wxPGProperty * baseparent,long flags) const12415 wxVariant wxPropertyGridState::DoGetPropertyValues( const wxString& listname,
12416                                                     wxPGProperty* baseparent,
12417                                                     long flags ) const
12418 {
12419     wxPGProperty* pwc = (wxPGProperty*) baseparent;
12420 
12421     // Root is the default base-parent.
12422     if ( !pwc )
12423         pwc = m_properties;
12424 
12425     wxVariantList tempList;
12426     wxVariant v( tempList, listname );
12427 
12428     if ( pwc->GetChildCount() )
12429     {
12430         if ( flags & wxPG_KEEP_STRUCTURE )
12431         {
12432             wxASSERT( !pwc->IsFlagSet(wxPG_PROP_AGGREGATE) );
12433 
12434             size_t i;
12435             for ( i=0; i<pwc->GetCount(); i++ )
12436             {
12437                 wxPGProperty* p = pwc->Item(i);
12438                 if ( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE) )
12439                 {
12440                     wxVariant variant = p->GetValue();
12441                     variant.SetName( p->GetBaseName() );
12442                     v.Append( variant );
12443                 }
12444                 else
12445                 {
12446                     v.Append( DoGetPropertyValues(p->m_name,p,flags|wxPG_KEEP_STRUCTURE) );
12447                 }
12448                 if ( (flags & wxPG_INC_ATTRIBUTES) && p->m_attributes.GetCount() )
12449                     v.Append( p->GetAttributesAsList() );
12450             }
12451         }
12452         else
12453         {
12454             wxPropertyGridConstIterator it( this, wxPG_ITERATE_DEFAULT, pwc->Item(0) );
12455             it.SetBaseParent( pwc );
12456 
12457             for ( ; !it.AtEnd(); it.Next() )
12458             {
12459                 const wxPGProperty* p = it.GetProperty();
12460 
12461                 // Use a trick to ignore wxParentProperty itself, but not its sub-properties.
12462                 if ( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE) )
12463                 {
12464                     wxVariant variant = p->GetValue();
12465                     variant.SetName( p->GetName() );
12466                     v.Append( variant );
12467                     if ( (flags & wxPG_INC_ATTRIBUTES) && p->m_attributes.GetCount() )
12468                         v.Append( p->GetAttributesAsList() );
12469                 }
12470             }
12471         }
12472     }
12473 
12474     return v;
12475 }
12476 
12477 // -----------------------------------------------------------------------
12478 
DoSetPropertyValues(const wxVariantList & list,wxPGProperty * defaultCategory)12479 void wxPropertyGridState::DoSetPropertyValues( const wxVariantList& list, wxPGProperty* defaultCategory )
12480 {
12481     unsigned char origFrozen = 1;
12482 
12483     if ( m_pPropGrid->GetState() == this )
12484     {
12485         origFrozen = m_pPropGrid->m_frozen;
12486         if ( !origFrozen ) m_pPropGrid->Freeze();
12487     }
12488 
12489     wxPropertyCategory* use_category = (wxPropertyCategory*)defaultCategory;
12490 
12491     if ( !use_category )
12492         use_category = (wxPropertyCategory*)m_properties;
12493 
12494     // Let's iterate over the list of variants.
12495     wxVariantList::const_iterator node;
12496     int numSpecialEntries = 0;
12497 
12498     //
12499     // Second pass for special entries
12500     for ( node = list.begin(); node != list.end(); node++ )
12501     {
12502         wxVariant *current = (wxVariant*)*node;
12503 
12504         // Make sure it is wxVariant.
12505         wxASSERT( current );
12506         wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 );
12507 
12508         const wxString& name = current->GetName();
12509         if ( name.length() > 0 )
12510         {
12511             //
12512             // '@' signified a special entry
12513             if ( name[0] == wxT('@') )
12514             {
12515                 numSpecialEntries++;
12516             }
12517             else
12518             {
12519                 wxPGProperty* foundProp = BaseGetPropertyByName(name);
12520                 if ( foundProp )
12521                 {
12522                     wxPGProperty* p = foundProp;
12523 
12524                     // If it was a list, we still have to go through it.
12525                     if ( wxStrcmp(current->GetType(), wxT("list")) == 0 )
12526                     {
12527                         DoSetPropertyValues( current->GetList(),
12528                                 p->IsCategory()?p:((wxPGProperty*)NULL)
12529                             );
12530                     }
12531                     else
12532                     {
12533                 #ifdef __WXDEBUG__
12534                         if ( wxStrcmp(current->GetType(), p->GetValue().GetType()) != 0)
12535                         {
12536                             wxLogDebug(wxT("wxPropertyGridState::DoSetPropertyValues Warning: Setting value of property \"%s\" from variant"),
12537                                 p->GetName().c_str());
12538                             //wxLogDebug(wxT("    but variant's type name (%s) doesn't match either base type name (%s) nor custom type name (%s)."),
12539                             //    current->GetType().c_str(),vtype->GetTypeName(),
12540                             //    vtype->GetCustomTypeName());
12541                         }
12542                 #endif
12543 
12544                         p->SetValue(*current);
12545                     }
12546                 }
12547                 else
12548                 {
12549                     // Is it list?
12550                     if ( current->GetType() != wxT("list") )
12551                     {
12552                         // Not.
12553                     }
12554                     else
12555                     {
12556                         // Yes, it is; create a sub category and append contents there.
12557                         wxPGProperty* newCat = DoInsert(use_category,-1,new wxPropertyCategory(current->GetName(),wxPG_LABEL));
12558                         DoSetPropertyValues( current->GetList(), newCat );
12559                     }
12560                 }
12561             }
12562         }
12563     }
12564 
12565     if ( numSpecialEntries )
12566     {
12567         for ( node = list.begin(); node != list.end(); node++ )
12568         {
12569             wxVariant *current = (wxVariant*)*node;
12570 
12571             const wxString& name = current->GetName();
12572             if ( name.length() > 0 )
12573             {
12574                 //
12575                 // '@' signified a special entry
12576                 if ( name[0] == wxT('@') )
12577                 {
12578                     numSpecialEntries--;
12579 
12580                     size_t pos2 = name.rfind(wxT('@'));
12581                     if ( pos2 > 0 && pos2 < (name.size()-1) )
12582                     {
12583                         wxString propName = name.substr(1, pos2-1);
12584                         wxString entryType = name.substr(pos2+1, wxString::npos);
12585 
12586                         if ( entryType == wxT("attr") )
12587                         {
12588                             //
12589                             // List of attributes
12590                             wxPGProperty* foundProp = BaseGetPropertyByName(propName);
12591                             if ( foundProp )
12592                             {
12593                                 wxASSERT( wxPGIsVariantType(*current, list) );
12594 
12595                                 wxVariantList& list2 = current->GetList();
12596                                 wxVariantList::const_iterator node2;
12597 
12598                                 for ( node2 = list2.begin(); node2 != list2.end(); node2++ )
12599                                 {
12600                                     wxVariant *attr = (wxVariant*)*node2;
12601                                     foundProp->SetAttribute( attr->GetName(), *attr );
12602                                 }
12603                             }
12604                             else
12605                             {
12606                                 // ERROR: No such property: 'propName'
12607                             }
12608                         }
12609                     }
12610                     else
12611                     {
12612                         // ERROR: Special entry requires name of format @<propname>@<entrytype>
12613                     }
12614                 }
12615             }
12616 
12617             if ( !numSpecialEntries )
12618                 break;
12619         }
12620     }
12621 
12622     if ( !origFrozen )
12623     {
12624         m_pPropGrid->Thaw();
12625 
12626         if ( this == m_pPropGrid->GetState() )
12627             m_pPropGrid->RefreshEditor();
12628     }
12629 
12630 }
12631 
12632 // -----------------------------------------------------------------------
12633 // wxPropertyGridState property adding and removal
12634 // -----------------------------------------------------------------------
12635 
12636 // Call for after sub-properties added with AddChild
PrepareSubProperties()12637 void wxPGProperty::PrepareSubProperties()
12638 {
12639     wxPropertyGridState* state = GetParentState();
12640 
12641     wxASSERT(state);
12642 
12643     if ( !GetCount() )
12644         return;
12645 
12646     wxByte depth = m_depth + 1;
12647     wxByte depthBgCol = m_depthBgCol;
12648 
12649     FlagType inheritFlags = m_flags & wxPG_INHERITED_PROPFLAGS;
12650 
12651     wxByte bgColIndex = m_bgColIndex;
12652     wxByte fgColIndex = m_fgColIndex;
12653 
12654     //
12655     // Set some values to the children
12656     //
12657     size_t i = 0;
12658     wxPGProperty* nparent = this;
12659 
12660     while ( i < nparent->GetCount() )
12661     {
12662         wxPGProperty* np = nparent->Item(i);
12663 
12664         np->m_parentState = state;
12665         np->m_flags |= inheritFlags; // Hideable also if parent.
12666         np->m_depth = depth;
12667         np->m_depthBgCol = depthBgCol;
12668         np->m_bgColIndex = bgColIndex;
12669         np->m_fgColIndex = fgColIndex;
12670 
12671         // Also handle children of children
12672         if ( np->GetCount() > 0 )
12673         {
12674             nparent = np;
12675             i = 0;
12676 
12677             // Init
12678             nparent->SetParentalType(wxPG_PROP_AGGREGATE);
12679             nparent->SetExpanded(false);
12680             depth++;
12681         }
12682         else
12683         {
12684             // Next sibling
12685             i++;
12686         }
12687 
12688         // After reaching last sibling, go back to processing
12689         // siblings of the parent
12690         while ( i >= nparent->GetCount() )
12691         {
12692             // Exit the loop when top parent hit
12693             if ( nparent == this )
12694                 break;
12695 
12696             depth--;
12697 
12698             i = nparent->GetArrIndex() + 1;
12699             nparent = nparent->GetParent();
12700         }
12701     }
12702 }
12703 
12704 // -----------------------------------------------------------------------
12705 
12706 // Call after fixed sub-properties added/removed after creation.
12707 // if oldSelInd >= 0 and < new max items, then selection is
12708 // moved to it. Note: oldSelInd -2 indicates that this property
12709 // should be selected.
SubPropsChanged(int oldSelInd)12710 void wxPGProperty::SubPropsChanged( int oldSelInd )
12711 {
12712     wxPropertyGridState* state = GetParentState();
12713     wxPropertyGrid* grid = state->GetGrid();
12714 
12715     PrepareSubProperties();
12716 
12717     wxPGProperty* sel = (wxPGProperty*) NULL;
12718     if ( oldSelInd >= (int)m_children.GetCount() )
12719         oldSelInd = (int)m_children.GetCount() - 1;
12720 
12721     if ( oldSelInd >= 0 )
12722         sel = (wxPGProperty*) m_children[oldSelInd];
12723     else if ( oldSelInd == -2 )
12724         sel = this;
12725 
12726     if ( sel )
12727         state->DoSelectProperty(sel);
12728 
12729     if ( state == grid->GetState() )
12730     {
12731         grid->GetPanel()->Refresh();
12732     }
12733 }
12734 
12735 // -----------------------------------------------------------------------
12736 
PrepareToAddItem(wxPGProperty * property,wxPGProperty * scheduledParent)12737 int wxPropertyGridState::PrepareToAddItem( wxPGProperty* property,
12738                                            wxPGProperty* scheduledParent )
12739 {
12740     wxPropertyGrid* propGrid = m_pPropGrid;
12741 
12742 #if wxPG_COMPATIBILITY_1_2_0
12743     // Make sure deprecated functions are not implemented
12744     wxASSERT_MSG( property->GetImageSize().x == -1234,
12745                   wxT("Custom properties implement OnMeasureImage instead of GetImageSize") );
12746     int oldCommonVal = property->m_commonValue;
12747     property->m_commonValue = -1;
12748     property->SetValueFromString( wxEmptyString, 0xFFFF );
12749     wxASSERT_MSG( property->m_commonValue == -1234,
12750                   wxT("Implement StringToValue() instead of SetValueFromString()") );
12751     property->m_commonValue = -1;
12752     property->SetValueFromInt( 0, 0xFFFF );
12753     wxASSERT_MSG( property->m_commonValue == -1234,
12754                   wxT("Implement StringToValue() instead of SetValueFromString()") );
12755     property->m_commonValue = oldCommonVal;
12756 #endif
12757 
12758     // This will allow better behavior.
12759     if ( scheduledParent == m_properties )
12760         scheduledParent = (wxPGProperty*) NULL;
12761 
12762     if ( scheduledParent && !scheduledParent->IsCategory() )
12763     {
12764         wxASSERT_MSG( property->GetBaseName().length(),
12765                       wxT("Property's children must have unique, non-empty names within their scope") );
12766     }
12767 
12768     property->m_parentState = this;
12769 
12770     if ( property->IsCategory() )
12771     {
12772 
12773         // Parent of a category must be either root or another category
12774         // (otherwise Bad Things might happen).
12775         wxASSERT_MSG( scheduledParent == NULL ||
12776                       scheduledParent == m_properties ||
12777                       scheduledParent->IsCategory(),
12778                  wxT("Parent of a category must be either root or another category."));
12779 
12780         // If we already have category with same name, delete given property
12781         // and use it instead as most recent caption item.
12782         wxPGProperty* found_id = BaseGetPropertyByName( property->GetBaseName() );
12783         if ( found_id )
12784         {
12785             wxPropertyCategory* pwc = (wxPropertyCategory*) found_id;
12786             if ( pwc->IsCategory() ) // Must be a category.
12787             {
12788                 delete property;
12789                 m_currentCategory = pwc;
12790                 return 2; // Tells the caller what we did.
12791             }
12792         }
12793     }
12794 
12795 #ifdef __WXDEBUG__
12796     // Warn for identical names in debug mode.
12797     if ( BaseGetPropertyByName(property->GetName()) &&
12798          (!scheduledParent || scheduledParent->IsCategory()) )
12799     {
12800         wxLogError(wxT("wxPropertyGrid: Warning - item with name \"%s\" already exists."),
12801             property->GetName().c_str());
12802         wxPGGlobalVars->m_warnings++;
12803     }
12804 #endif
12805 
12806     if ( scheduledParent )
12807     {
12808         // Use parent's colours.
12809         property->m_bgColIndex = scheduledParent->m_bgColIndex;
12810         property->m_fgColIndex = scheduledParent->m_fgColIndex;
12811 
12812         // Fix no parent does not yet have parenting flag yet, set one now
12813         if ( !scheduledParent->HasFlag(wxPG_PROP_PARENTAL_FLAGS) )
12814             scheduledParent->SetParentalType(wxPG_PROP_MISC_PARENT);
12815             //scheduledParent->SetFlag(wxPG_PROP_MISC_PARENT);
12816     }
12817 
12818     // If in hideable adding mode, or if assigned parent is hideable, then
12819     // make this one hideable.
12820     if (
12821          ( scheduledParent && (scheduledParent->m_flags & wxPG_PROP_HIDDEN) ) ||
12822          ( propGrid && (propGrid->m_iFlags & wxPG_FL_ADDING_HIDEABLES) )
12823        )
12824         property->SetFlag( wxPG_PROP_HIDDEN );
12825 
12826     // Set custom image flag.
12827     int custImgHeight = property->OnMeasureImage().y;
12828     if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
12829     {
12830         property->m_flags |= wxPG_PROP_CUSTOMIMAGE;
12831     }
12832 
12833     if ( propGrid && (propGrid->GetWindowStyleFlag() & wxPG_LIMITED_EDITING) )
12834         property->m_flags |= wxPG_PROP_NOEDITOR;
12835 
12836     if ( !property->IsCategory() )
12837     {
12838         // This is not a category.
12839 
12840         //wxASSERT_MSG( property->GetEditorClass(), wxT("Editor class not initialized!") );
12841 
12842         // Depth.
12843         //
12844         unsigned char depth = 1;
12845         if ( scheduledParent )
12846         {
12847             depth = scheduledParent->m_depth;
12848             if ( !scheduledParent->IsCategory() )
12849                 depth++;
12850         }
12851         property->m_depth = depth;
12852         unsigned char greyDepth = depth;
12853 
12854         if ( scheduledParent )
12855         {
12856             wxPropertyCategory* pc;
12857 
12858             if ( scheduledParent->IsCategory() || scheduledParent->IsRoot() )
12859                 pc = (wxPropertyCategory*)scheduledParent;
12860             else
12861                 // This conditional compile is necessary to
12862                 // bypass some compiler bug.
12863                 pc = GetPropertyCategory(scheduledParent);
12864 
12865             if ( pc )
12866                 greyDepth = pc->GetDepth();
12867             else
12868                 greyDepth = scheduledParent->m_depthBgCol;
12869         }
12870 
12871         property->m_depthBgCol = greyDepth;
12872 
12873         // Prepare children pre-added children
12874         if ( property->GetCount() )
12875         {
12876             property->SetParentalType(wxPG_PROP_AGGREGATE);
12877 
12878             property->SetExpanded(false); // Properties with children are not expanded by default.
12879             if ( propGrid && propGrid->GetWindowStyleFlag() & wxPG_HIDE_MARGIN )
12880                 property->SetExpanded(true); // ...unless it cannot be expanded.
12881 
12882             property->PrepareSubProperties();
12883 
12884             return -1;
12885         }
12886 
12887         if ( propGrid && (propGrid->GetExtraStyle() & wxPG_EX_AUTO_UNSPECIFIED_VALUES) )
12888             property->SetFlagRecursively(wxPG_PROP_AUTO_UNSPECIFIED, true);
12889 
12890         return 0;
12891     }
12892     else
12893     {
12894         // This is a category.
12895 
12896         // depth
12897         unsigned char depth = 1;
12898         if ( scheduledParent )
12899         {
12900             depth = scheduledParent->m_depth + 1;
12901         }
12902         property->m_depth = depth;
12903         property->m_depthBgCol = depth;
12904 
12905         m_currentCategory = (wxPropertyCategory*)property;
12906 
12907         wxPropertyCategory* pc = (wxPropertyCategory*)property;
12908 
12909         // Calculate text extent for caption item.
12910         if ( propGrid )
12911             pc->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
12912 
12913         return 1;
12914     }
12915 }
12916 
12917 // -----------------------------------------------------------------------
12918 
BeginAddChildren(wxPGPropArg id)12919 void wxPropertyGridInterface::BeginAddChildren( wxPGPropArg id )
12920 {
12921     wxPG_PROP_ARG_CALL_PROLOG()
12922     wxCHECK_RET( p->HasFlag(wxPG_PROP_AGGREGATE), wxT("only call on properties with fixed children") );
12923     p->ClearFlag(wxPG_PROP_AGGREGATE);
12924     p->SetFlag(wxPG_PROP_MISC_PARENT);
12925 }
12926 
12927 // -----------------------------------------------------------------------
12928 
EndAddChildren(wxPGPropArg id)12929 void wxPropertyGridInterface::EndAddChildren( wxPGPropArg id )
12930 {
12931     wxPG_PROP_ARG_CALL_PROLOG()
12932     wxCHECK_RET( p->HasFlag(wxPG_PROP_MISC_PARENT), wxT("only call on properties for which BeginAddChildren was called prior") );
12933     p->ClearFlag(wxPG_PROP_MISC_PARENT);
12934     p->SetFlag(wxPG_PROP_AGGREGATE);
12935 }
12936 
12937 // -----------------------------------------------------------------------
12938 
DoAppend(wxPGProperty * property)12939 wxPGProperty* wxPropertyGridState::DoAppend( wxPGProperty* property )
12940 {
12941     wxPropertyCategory* cur_cat = m_currentCategory;
12942     if ( property->IsCategory() )
12943         cur_cat = (wxPropertyCategory*) NULL;
12944 
12945     return DoInsert( cur_cat, -1, property );
12946 }
12947 
12948 // -----------------------------------------------------------------------
12949 
DoInsert(wxPGProperty * parent,int index,wxPGProperty * property)12950 wxPGProperty* wxPropertyGridState::DoInsert( wxPGProperty* parent, int index, wxPGProperty* property )
12951 {
12952     if ( !parent )
12953         parent = m_properties;
12954 
12955     wxCHECK_MSG( !parent->HasFlag(wxPG_PROP_AGGREGATE),
12956                  wxNullProperty,
12957                  wxT("when adding properties to fixed parents, use BeginAddChildren and EndAddChildren.") );
12958 
12959     int parenting = PrepareToAddItem( property, (wxPropertyCategory*)parent );
12960 
12961     // This type of invalid parenting value indicates we should exit now, returning
12962     // id of most recent category.
12963     if ( parenting > 1 )
12964         return m_currentCategory;
12965 
12966     // Note that item must be added into current mode later.
12967 
12968     // If parent is wxParentProperty, just stick it in...
12969     // If parent is root (m_properties), then...
12970     //   In categoric mode: Add as last item in m_abcArray (if not category).
12971     //                      Add to given index in m_regularArray.
12972     //   In non-cat mode:   Add as last item in m_regularArray.
12973     //                      Add to given index in m_abcArray.
12974     // If parent is category, then...
12975     //   1) Add to given category in given index.
12976     //   2) Add as last item in m_abcArray.
12977 
12978     if ( !parent->IsCategory() && !parent->IsRoot() )
12979     {
12980         // Parent is wxParentingProperty: Just stick it in...
12981         parent->AddChild2( property, index );
12982     }
12983     else
12984     {
12985         // Parent is Category or Root.
12986 
12987         if ( m_properties == &m_regularArray )
12988         {
12989             // Categorized mode
12990 
12991             // Only add non-categories to m_abcArray.
12992             if ( m_abcArray && parenting <= 0 )
12993                 m_abcArray->AddChild2( property, -1, false );
12994 
12995             // Add to current mode.
12996             parent->AddChild2( property, index );
12997 
12998         }
12999         else
13000         {
13001             // Non-categorized mode.
13002 
13003             if ( parent != m_properties )
13004                 // Parent is category.
13005                 parent->AddChild2( property, index, false );
13006             else
13007                 // Parent is root.
13008                 m_regularArray.AddChild2( property, -1, false );
13009 
13010             // Add to current mode (no categories).
13011             if ( parenting <= 0 )
13012                 m_abcArray->AddChild2( property, index );
13013         }
13014     }
13015 
13016     // category stuff
13017     if ( property->IsCategory() )
13018     {
13019         // This is a category caption item.
13020 
13021         // Last caption is not the bottom one (this info required by append)
13022         m_lastCaptionBottomnest = 0;
13023     }
13024 
13025     // Only add name to hashmap if parent is root or category
13026     if ( property->m_name.length() &&
13027          (parent->IsCategory() || parent->IsRoot()) )
13028         m_dictName[property->m_name] = (void*) property;
13029 
13030     VirtualHeightChanged();
13031 
13032     property->UpdateParentValues();
13033 
13034     // Fix y-position of any open property editor
13035     if ( m_pPropGrid )
13036         m_pPropGrid->CorrectEditorWidgetPosY();
13037 
13038     m_itemsAdded = 1;
13039 
13040     return property;
13041 }
13042 
13043 // -----------------------------------------------------------------------
13044 
DoDelete(wxPGProperty * item,bool doDelete)13045 void wxPropertyGridState::DoDelete( wxPGProperty* item, bool doDelete )
13046 {
13047     //wxLogDebug(wxT("DoDelete: %s"), item->GetLabel().c_str());
13048 
13049     wxCHECK_RET( item->GetParent(),
13050         wxT("this property was already deleted") );
13051 
13052     wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
13053         wxT("wxPropertyGrid: Do not attempt to remove the root item.") );
13054 
13055     wxASSERT( item->GetParentState() == this );
13056 
13057 	wxPropertyGrid* pg = GetGrid();
13058 
13059     // Must defer deletion? Yes, if handling a wxPG event.
13060     if ( pg && pg->m_processedEvent )
13061     {
13062         if ( doDelete )
13063             pg->m_deletedProperties.push_back(item);
13064         else
13065             pg->m_removedProperties.push_back(item);
13066 
13067         // Rename the property so it won't remain in the way
13068         // of the user code.
13069 
13070         // Let's trust that no sane property uses prefix like
13071         // this. It would be anyway fairly inconvenient (in
13072         // current code) to check whether a new name is used
13073         // by another property with parent (due to the child
13074         // name notation).
13075         wxString newName = wxT("_&/_%$") + item->GetBaseName();
13076         pg->DoSetPropertyName(item, newName);
13077 
13078         return;
13079     }
13080 
13081     if ( DoIsPropertySelected(item) )
13082     {
13083         if ( pg && pg->GetState() == this )
13084         {
13085             pg->DoRemoveFromSelection(item,
13086                 wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE);
13087         }
13088         else
13089         {
13090             DoRemoveFromSelection(item);
13091         }
13092     }
13093 
13094     item->SetFlag(wxPG_PROP_BEING_DELETED);
13095 
13096     unsigned int indinparent = item->GetIndexInParent();
13097 
13098     wxPGProperty* pwc = (wxPGProperty*)item;
13099     wxPGProperty* parent = item->GetParent();
13100 
13101     wxCHECK_RET( !parent->HasFlag(wxPG_PROP_AGGREGATE),
13102         wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") );
13103 
13104     // Delete children
13105     if ( item->GetChildCount() && !item->HasFlag(wxPG_PROP_AGGREGATE) )
13106     {
13107         // deleting a category
13108         if ( item->IsCategory() )
13109         {
13110             if ( pwc == m_currentCategory )
13111                 m_currentCategory = (wxPropertyCategory*) NULL;
13112         }
13113 
13114         item->DeleteChildren();
13115     }
13116 
13117     if ( !IsInNonCatMode() )
13118     {
13119         // categorized mode - non-categorized array
13120 
13121         // Remove from non-cat array
13122         if ( !item->IsCategory() &&
13123              (parent->IsCategory() || parent->IsRoot()) )
13124         {
13125             if ( m_abcArray )
13126             {
13127                 m_abcArray->m_children.Remove( item );
13128             }
13129         }
13130 
13131         // categorized mode - categorized array
13132         parent->m_children.RemoveAt(indinparent);
13133         parent->FixIndexesOfChildren();
13134     }
13135     else
13136     {
13137         // non-categorized mode - categorized array
13138 
13139         // We need to find location of item.
13140         wxPGProperty* cat_parent = &m_regularArray;
13141         int cat_index = m_regularArray.GetCount();
13142         size_t i;
13143         for ( i = 0; i < m_regularArray.GetCount(); i++ )
13144         {
13145             wxPGProperty* p = m_regularArray.Item(i);
13146             if ( p == item ) { cat_index = i; break; }
13147             if ( p->IsCategory() )
13148             {
13149                 int subind = ((wxPGProperty*)p)->Index(item);
13150                 if ( subind != wxNOT_FOUND )
13151                 {
13152                     cat_parent = ((wxPGProperty*)p);
13153                     cat_index = subind;
13154                     break;
13155                 }
13156             }
13157         }
13158         cat_parent->m_children.RemoveAt(cat_index);
13159 
13160         // non-categorized mode - non-categorized array
13161         if ( !item->IsCategory() )
13162         {
13163             wxASSERT( item->m_parent == m_abcArray );
13164             item->m_parent->m_children.RemoveAt(indinparent);
13165             item->m_parent->FixIndexesOfChildren(indinparent);
13166         }
13167     }
13168 
13169     if ( item->GetBaseName().length() &&
13170          (parent->IsCategory() || parent->IsRoot()) )
13171         m_dictName.erase( wxPGNameConv(item->GetBaseName()) );
13172 
13173 #ifdef __WXPYTHON__
13174     // For some reason, Py_DECREF always crashes, even though we make
13175     // matching Py_INCREF call in propgrid_cbacks.cpp. Maybe refcount is decremented
13176     // somewhere automatically? Unlikely though...
13177     //if ( item->m_scriptObject )
13178     //    Py_DECREF( item->m_scriptObject );
13179 #endif
13180 
13181 	// We need to clear parent grid's m_propHover, if it matches item
13182 	if ( pg && pg->m_propHover == item )
13183 		pg->m_propHover = NULL;
13184 
13185     // Mark the property as 'unattached'
13186     item->m_parentState = NULL;
13187     item->m_parent = NULL;
13188 
13189     // We can actually delete it now
13190     if ( doDelete )
13191         delete item;
13192 
13193     m_itemsAdded = 1; // Not a logical assignment (but required nonetheless).
13194 
13195     VirtualHeightChanged();
13196 }
13197 
13198 // -----------------------------------------------------------------------
13199 // wxPropertyGridState init etc.
13200 // -----------------------------------------------------------------------
13201 
InitNonCatMode()13202 void wxPropertyGridState::InitNonCatMode()
13203 {
13204     if ( !m_abcArray )
13205     {
13206         m_abcArray = new wxPGRootProperty();
13207         m_abcArray->SetParentState(this);
13208         m_abcArray->SetFlag(wxPG_PROP_CHILDREN_ARE_COPIES);
13209     }
13210 
13211     // Must be called when state::m_properties still points to regularArray.
13212     wxPGProperty* oldProperties = m_properties;
13213 
13214     // Must use temp value in state::m_properties for item iteration loop
13215     // to run as expected.
13216     m_properties = &m_regularArray;
13217 
13218     if ( m_properties->GetChildCount() )
13219     {
13220         //
13221         // Prepare m_abcArray
13222         wxPropertyGridIterator it( this, wxPG_ITERATE_PROPERTIES );
13223 
13224         for ( ; !it.AtEnd(); it.Next() )
13225         {
13226             wxPGProperty* p = it.GetProperty();
13227             wxPGProperty* parent = p->GetParent();
13228             if ( parent->IsCategory() || parent->IsRoot() )
13229             {
13230                 m_abcArray->AddChild2(p);
13231                 p->m_parent = &m_regularArray;
13232             }
13233         }
13234     }
13235 
13236     m_properties = oldProperties;
13237 }
13238 
13239 // -----------------------------------------------------------------------
13240 
DoClear()13241 void wxPropertyGridState::DoClear()
13242 {
13243     if ( m_pPropGrid && m_pPropGrid->GetState() == this )
13244     {
13245         m_pPropGrid->DoSelectProperty(NULL,
13246                                       wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE);
13247     }
13248     else
13249     {
13250         m_selection.clear();
13251     }
13252 
13253     m_regularArray.DoEmpty();
13254     if ( m_abcArray )
13255         m_abcArray->DoEmpty();
13256 
13257     m_dictName.clear();
13258 
13259     m_currentCategory = (wxPropertyCategory*) NULL;
13260     m_lastCaptionBottomnest = 1;
13261     m_itemsAdded = 0;
13262 
13263     m_virtualHeight = 0;
13264     m_vhCalcPending = 0;
13265 }
13266 
13267 // -----------------------------------------------------------------------
13268 
wxPropertyGridState()13269 wxPropertyGridState::wxPropertyGridState()
13270 {
13271     m_pPropGrid = (wxPropertyGrid*) NULL;
13272     m_regularArray.SetParentState(this);
13273     m_properties = &m_regularArray;
13274     m_abcArray = (wxPGRootProperty*) NULL;
13275     m_currentCategory = (wxPropertyCategory*) NULL;
13276     m_width = 0;
13277     m_virtualHeight = 0;
13278     m_lastCaptionBottomnest = 1;
13279     m_itemsAdded = 0;
13280     m_anyModified = 0;
13281     m_vhCalcPending = 0;
13282     m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
13283     m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
13284     m_fSplitterX = wxPG_DEFAULT_SPLITTERX;
13285 
13286     m_columnProportions.push_back(1);
13287     m_columnProportions.push_back(1);
13288 
13289     // By default, we only have the 'value' column editable
13290     m_editableColumns.push_back(1);
13291 }
13292 
13293 // -----------------------------------------------------------------------
13294 
~wxPropertyGridState()13295 wxPropertyGridState::~wxPropertyGridState()
13296 {
13297     delete m_abcArray;
13298 }
13299 
13300 // -----------------------------------------------------------------------
13301 
CalculateFontAndBitmapStuff(int WXUNUSED (vspacing))13302 void wxPropertyGridState::CalculateFontAndBitmapStuff( int WXUNUSED(vspacing) )
13303 {
13304     wxPropertyGrid* propGrid = GetGrid();
13305 
13306     VirtualHeightChanged();
13307 
13308     // Recalculate caption text extents.
13309     unsigned int i;
13310 
13311     for ( i=0;i<m_regularArray.GetCount();i++ )
13312     {
13313         wxPGProperty* p =m_regularArray.Item(i);
13314 
13315         if ( p->IsCategory() )
13316             ((wxPropertyCategory*)p)->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
13317     }
13318 }
13319 
13320 // -----------------------------------------------------------------------
13321 // wxPropertyGridPopulator
13322 // -----------------------------------------------------------------------
13323 
wxPropertyGridPopulator()13324 wxPropertyGridPopulator::wxPropertyGridPopulator()
13325 {
13326     m_state = NULL;
13327     m_pg = NULL;
13328     wxPGGlobalVars->m_offline++;
13329 }
13330 
13331 // -----------------------------------------------------------------------
13332 
SetState(wxPropertyGridState * state)13333 void wxPropertyGridPopulator::SetState( wxPropertyGridState* state )
13334 {
13335     m_state = state;
13336     m_propHierarchy.clear();
13337 }
13338 
13339 // -----------------------------------------------------------------------
13340 
SetGrid(wxPropertyGrid * pg)13341 void wxPropertyGridPopulator::SetGrid( wxPropertyGrid* pg )
13342 {
13343     m_pg = pg;
13344     pg->Freeze();
13345 }
13346 
13347 // -----------------------------------------------------------------------
13348 
~wxPropertyGridPopulator()13349 wxPropertyGridPopulator::~wxPropertyGridPopulator()
13350 {
13351     //
13352     // Free unused sets of choices
13353     wxPGHashMapS2P::iterator it;
13354 
13355     for( it = m_dictIdChoices.begin(); it != m_dictIdChoices.end(); ++it )
13356     {
13357         wxPGChoicesData* data = (wxPGChoicesData*) it->second;
13358         data->DecRef();
13359     }
13360 
13361     if ( m_pg )
13362     {
13363         m_pg->Thaw();
13364         m_pg->GetPanel()->Refresh();
13365     }
13366     wxPGGlobalVars->m_offline--;
13367 }
13368 
13369 // -----------------------------------------------------------------------
13370 
Add(const wxString & propClass,const wxString & propLabel,const wxString & propName,const wxString * propValue,wxPGChoices * pChoices)13371 wxPGProperty* wxPropertyGridPopulator::Add( const wxString& propClass,
13372                                             const wxString& propLabel,
13373                                             const wxString& propName,
13374                                             const wxString* propValue,
13375                                             wxPGChoices* pChoices )
13376 {
13377     wxClassInfo* classInfo = wxClassInfo::FindClass(propClass);
13378     wxPGProperty* parent = GetCurParent();
13379 
13380     if ( parent->HasFlag(wxPG_PROP_AGGREGATE) )
13381     {
13382         ProcessError(wxString::Format(wxT("new children cannot be added to '%s'"),parent->GetName().c_str()));
13383         return NULL;
13384     }
13385 
13386     if ( !classInfo || !classInfo->IsKindOf(CLASSINFO(wxPGProperty)) )
13387     {
13388         ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass.c_str()));
13389         return NULL;
13390     }
13391 
13392     wxPGProperty* property = (wxPGProperty*) classInfo->CreateObject();
13393 
13394     property->SetLabel(propLabel);
13395     property->DoSetName(propName);
13396 
13397     if ( pChoices && pChoices->IsOk() )
13398         property->SetChoices(*pChoices);
13399 
13400     m_state->DoInsert(parent, -1, property);
13401 
13402     if ( propValue )
13403         property->SetValueFromString( *propValue, wxPG_FULL_VALUE|
13404                                                   wxPG_PROGRAMMATIC_VALUE );
13405 
13406     return property;
13407 }
13408 
13409 // -----------------------------------------------------------------------
13410 
AddChildren(wxPGProperty * property)13411 void wxPropertyGridPopulator::AddChildren( wxPGProperty* property )
13412 {
13413     m_propHierarchy.push_back(property);
13414     DoScanForChildren();
13415     m_propHierarchy.pop_back();
13416 }
13417 
13418 // -----------------------------------------------------------------------
13419 
ParseChoices(const wxString & choicesString,const wxString & idString)13420 wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString,
13421                                                    const wxString& idString )
13422 {
13423     wxPGChoices choices;
13424 
13425     // Using id?
13426     if ( choicesString[0] == wxT('@') )
13427     {
13428         wxString ids = choicesString.substr(1);
13429         wxPGHashMapS2P::iterator it = m_dictIdChoices.find(ids);
13430         if ( it == m_dictIdChoices.end() )
13431             ProcessError(wxString::Format(wxT("No choices defined for id '%s'"),ids.c_str()));
13432         else
13433             choices.AssignData((wxPGChoicesData*)it->second);
13434     }
13435     else
13436     {
13437         bool found = false;
13438         if ( idString.length() )
13439         {
13440             wxPGHashMapS2P::iterator it = m_dictIdChoices.find(idString);
13441             if ( it != m_dictIdChoices.end() )
13442             {
13443                 choices.AssignData((wxPGChoicesData*)it->second);
13444                 found = true;
13445             }
13446         }
13447 
13448         if ( !found )
13449         {
13450             // Parse choices string
13451             wxString::const_iterator it = choicesString.begin();
13452             wxString label;
13453             wxString value;
13454             int state = 0;
13455             bool labelValid = false;
13456 
13457             for ( ; it != choicesString.end(); it++ )
13458             {
13459                 wxChar c = *it;
13460 
13461                 if ( state != 1 )
13462                 {
13463                     if ( c == wxT('"') )
13464                     {
13465                         if ( labelValid )
13466                         {
13467                             long l;
13468                             if ( !value.ToLong(&l, 0) ) l = wxPG_INVALID_VALUE;
13469                             choices.Add(label, l);
13470                         }
13471                         labelValid = false;
13472                         //wxLogDebug(wxT("%s, %s"),label.c_str(),value.c_str());
13473                         value.clear();
13474                         label.clear();
13475                         state = 1;
13476                     }
13477                     else if ( c == wxT('=') )
13478                     {
13479                         if ( labelValid )
13480                         {
13481                             state = 2;
13482                         }
13483                     }
13484                     else if ( state == 2 && (wxIsalnum(c) || c == wxT('x')) )
13485                     {
13486                         value << c;
13487                     }
13488                 }
13489                 else
13490                 {
13491                     if ( c == wxT('"') )
13492                     {
13493                         state = 0;
13494                         labelValid = true;
13495                     }
13496                     else
13497                         label << c;
13498                 }
13499             }
13500 
13501             if ( labelValid )
13502             {
13503                 long l;
13504                 if ( !value.ToLong(&l, 0) ) l = wxPG_INVALID_VALUE;
13505                 choices.Add(label, l);
13506             }
13507 
13508             if ( !choices.IsOk() )
13509             {
13510                 choices.EnsureData();
13511             }
13512 
13513             // Assign to id
13514             if ( idString.length() )
13515                 m_dictIdChoices[idString] = choices.GetData();
13516         }
13517     }
13518 
13519     return choices;
13520 }
13521 
13522 // -----------------------------------------------------------------------
13523 
ToLongPCT(const wxString & s,long * pval,long max)13524 bool wxPropertyGridPopulator::ToLongPCT( const wxString& s, long* pval, long max )
13525 {
13526     if ( s.Last() == wxT('%') )
13527     {
13528         wxString s2 = s.substr(0,s.length()-1);
13529         long val;
13530         if ( s2.ToLong(&val, 10) )
13531         {
13532             *pval = (val*max)/100;
13533             return true;
13534         }
13535         return false;
13536     }
13537 
13538     return s.ToLong(pval, 10);
13539 }
13540 
13541 // -----------------------------------------------------------------------
13542 
AddAttribute(const wxString & name,const wxString & type,const wxString & value)13543 bool wxPropertyGridPopulator::AddAttribute( const wxString& name,
13544                                             const wxString& type,
13545                                             const wxString& value )
13546 {
13547     int l = m_propHierarchy.size();
13548     if ( !l )
13549         return false;
13550 
13551     wxPGProperty* p = m_propHierarchy[l-1];
13552     wxString valuel = value.Lower();
13553     wxVariant variant;
13554 
13555     if ( type.length() == 0 )
13556     {
13557         long v;
13558 
13559         // Auto-detect type
13560         if ( valuel == wxT("true") || valuel == wxT("yes") || valuel == wxT("1") )
13561             variant = true;
13562         else if ( valuel == wxT("false") || valuel == wxT("no") || valuel == wxT("0") )
13563             variant = false;
13564         else if ( value.ToLong(&v, 0) )
13565             variant = v;
13566         else
13567             variant = value;
13568     }
13569     else
13570     {
13571         if ( type == wxT("string") )
13572         {
13573             variant = value;
13574         }
13575         else if ( type == wxT("int") )
13576         {
13577             long v = 0;
13578             value.ToLong(&v, 0);
13579             variant = v;
13580         }
13581         else if ( type == wxT("bool") )
13582         {
13583             if ( valuel == wxT("true") || valuel == wxT("yes") || valuel == wxT("1") )
13584                 variant = true;
13585             else
13586                 variant = false;
13587         }
13588         else
13589         {
13590             ProcessError(wxString::Format(wxT("Invalid attribute type '%s'"),type.c_str()));
13591             return false;
13592         }
13593     }
13594 
13595     p->SetAttribute( name, variant );
13596 
13597     return true;
13598 }
13599 
13600 // -----------------------------------------------------------------------
13601 
ProcessError(const wxString & msg)13602 void wxPropertyGridPopulator::ProcessError( const wxString& msg )
13603 {
13604     wxLogError(_("Error in resource: %s"),msg.c_str());
13605 }
13606 
13607 // -----------------------------------------------------------------------
13608 // Implementation of various deprecated methods that were inline,
13609 // made non-inline to eliminate problems with wxDEPRECATED.
13610 // -----------------------------------------------------------------------
13611 
13612 #if wxPG_COMPATIBILITY_1_2_0
13613 
AppendCategory(const wxString & label,const wxString & name)13614 wxPGProperty* wxPropertyGridInterface::AppendCategory( const wxString& label, const wxString& name )
13615 {
13616     return Append( new wxPropertyCategory(label,name) );
13617 }
13618 
Delete(wxPGPropArg id)13619 void wxPropertyGridInterface::Delete( wxPGPropArg id )
13620 {
13621     DeleteProperty(id);
13622 }
13623 
Disable(wxPGPropArg id)13624 bool wxPropertyGridInterface::Disable( wxPGPropArg id )
13625 {
13626     return EnableProperty(id,false);
13627 }
13628 
IsPropertyValueType(wxPGPropArg id,const wxChar * typestr) const13629 bool wxPropertyGridInterface::IsPropertyValueType( wxPGPropArg id, const wxChar* typestr ) const
13630 {
13631     wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
13632     return (wxStrcmp(p->GetValue().GetType(),typestr) == 0);
13633 }
13634 
IsModified(wxPGPropArg id) const13635 bool wxPropertyGridInterface::IsModified( wxPGPropArg id ) const
13636 {
13637     return IsPropertyModified(id);
13638 }
13639 
ClearTargetPage()13640 void wxPropertyGrid::ClearTargetPage()
13641 {
13642     Clear();
13643 }
13644 
SetPropertyColour(wxPGPropArg id,const wxColour & col)13645 void wxPropertyGrid::SetPropertyColour( wxPGPropArg id, const wxColour& col )
13646 {
13647     SetPropertyBackgroundColour( id, col );
13648 }
13649 
13650 #endif  // wxPG_COMPATIBILITY_1_2_0
13651 
13652 // -----------------------------------------------------------------------
13653 
13654