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