1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        samples/propgrid/sampleprops.cpp
3 // Purpose:     wxPropertyGrid Sample Properties
4 // Author:      Jaakko Salli
5 // Modified by:
6 // Created:     2006-03-05
7 // Copyright:   (c) Jaakko Salli
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
13 
14 #ifdef __BORLANDC__
15     #pragma hdrstop
16 #endif
17 
18 // for all others, include the necessary headers (this file is usually all you
19 // need because it includes almost all "standard" wxWidgets headers)
20 #ifndef WX_PRECOMP
21     #include "wx/wx.h"
22 #endif
23 
24 #include "wx/fontdlg.h"
25 
26 // -----------------------------------------------------------------------
27 
28 
29 #include <wx/propgrid/propgrid.h>
30 #include <wx/propgrid/advprops.h>
31 
32 #ifndef WX_PROPGRID_SAMPLEPROPS_H
33     #include "sampleprops.h"
34 #endif
35 
36 
37 // -----------------------------------------------------------------------
38 // wxFontDataProperty
39 // -----------------------------------------------------------------------
40 
41 // Dummy comparison required by value type implementation.
operator ==(const wxFontData &,const wxFontData &)42 bool operator == (const wxFontData&, const wxFontData&)
43 {
44     return FALSE;
45 }
46 
47 // Custom version of wxFontProperty that also holds colour in the value.
48 // Original version by Vladimir Vainer.
49 
50 IMPLEMENT_VARIANT_OBJECT_SHALLOWCMP(wxFontData)
51 
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFontDataProperty,wxFontProperty,wxFontData,const wxFontData &,TextCtrlAndButton)52 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFontDataProperty,wxFontProperty,
53                                wxFontData,const wxFontData&,TextCtrlAndButton)
54 
55 wxFontDataProperty::wxFontDataProperty( const wxString& label, const wxString& name,
56    const wxFontData& value ) : wxFontProperty(label,name,value.GetInitialFont())
57 {
58     wxFontData fontData(value);
59 
60     // Fix value.
61     fontData.SetChosenFont(value.GetInitialFont());
62     if ( !fontData.GetColour().IsOk() )
63         fontData.SetColour(*wxBLACK);
64 
65     // Set initial value - should be done in a simpler way like this
66     // (instead of calling SetValue) in derived (wxObject) properties.
67     m_value_wxFontData << value;
68 
69     // Add extra children.
70     AddPrivateChild( new wxColourProperty(_("Colour"), wxPG_LABEL,
71                                           fontData.GetColour() ) );
72 }
73 
~wxFontDataProperty()74 wxFontDataProperty::~wxFontDataProperty () { }
75 
OnSetValue()76 void wxFontDataProperty::OnSetValue()
77 {
78     if ( m_value.GetType() != "wxFontData" )
79     {
80         if ( m_value.GetType() == "wxFont" )
81         {
82             wxFont font;
83             font << m_value;
84             wxFontData fontData;
85             fontData.SetChosenFont(font);
86             if ( !m_value_wxFontData.IsNull() )
87             {
88                 wxFontData oldFontData;
89                 oldFontData << m_value_wxFontData;
90                 fontData.SetColour(oldFontData.GetColour());
91             }
92             else
93             {
94                 fontData.SetColour(*wxBLACK);
95             }
96             wxVariant variant;
97             variant << fontData;
98             m_value_wxFontData = variant;
99         }
100         else
101         {
102             wxFAIL_MSG(wxT("Value to wxFontDataProperty must be eithe wxFontData or wxFont"));
103         }
104     }
105     else
106     {
107         // Set m_value to wxFont so that wxFontProperty methods will work
108         // correctly.
109         m_value_wxFontData = m_value;
110 
111         wxFontData fontData;
112         fontData << m_value_wxFontData;
113 
114         wxFont font = fontData.GetChosenFont();
115         if ( !font.IsOk() )
116             font = wxFont(10,wxSWISS,wxNORMAL,wxNORMAL);
117 
118         m_value = WXVARIANT(font);
119     }
120 }
121 
DoGetValue() const122 wxVariant wxFontDataProperty::DoGetValue() const
123 {
124     return m_value_wxFontData;
125 }
126 
127 // Must re-create font dialog displayer.
OnEvent(wxPropertyGrid * propgrid,wxWindow * WXUNUSED (primary),wxEvent & event)128 bool wxFontDataProperty::OnEvent( wxPropertyGrid* propgrid,
129                                   wxWindow* WXUNUSED(primary), wxEvent& event )
130 {
131     if ( propgrid->IsMainButtonEvent(event) )
132     {
133         wxVariant useValue = propgrid->GetUncommittedPropertyValue();
134 
135         wxFontData fontData;
136         fontData << useValue;
137 
138         fontData.SetInitialFont(fontData.GetChosenFont());
139 
140         wxFontDialog dlg(propgrid, fontData);
141 
142         if ( dlg.ShowModal() == wxID_OK )
143         {
144             wxVariant variant;
145             variant << dlg.GetFontData();
146             SetValueInEvent( variant );
147             return true;
148         }
149     }
150     return false;
151 }
152 
RefreshChildren()153 void wxFontDataProperty::RefreshChildren()
154 {
155     wxFontProperty::RefreshChildren();
156     if ( GetChildCount() < 6 ) // Number is count of wxFontProperty's children + 1.
157         return;
158     wxFontData fontData; fontData << m_value_wxFontData;
159     wxVariant variant; variant << fontData.GetColour();
160     Item(6)->SetValue( variant );
161 }
162 
ChildChanged(wxVariant & thisValue,int childIndex,wxVariant & childValue) const163 wxVariant wxFontDataProperty::ChildChanged( wxVariant& thisValue,
164                                             int childIndex,
165                                             wxVariant& childValue ) const
166 {
167     wxFontData fontData;
168     fontData << thisValue;
169     wxColour col;
170     wxVariant variant;
171 
172     switch ( childIndex )
173     {
174         case 6:
175             col << childValue;
176             fontData.SetColour( col );
177             break;
178         default:
179             // Transfer from subset to superset.
180             wxFont font = fontData.GetChosenFont();
181             variant = WXVARIANT(font);
182             wxFontProperty::ChildChanged( variant, childIndex, childValue );
183             font << variant;
184             fontData.SetChosenFont(font);
185     }
186 
187     wxVariant newVariant;
188     newVariant << fontData;
189     return newVariant;
190 }
191 
192 // -----------------------------------------------------------------------
193 // wxSizeProperty
194 // -----------------------------------------------------------------------
195 
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxSizeProperty,wxPGProperty,wxSize,const wxSize &,TextCtrl)196 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxSizeProperty,wxPGProperty,
197                                wxSize,const wxSize&,TextCtrl)
198 
199 wxSizeProperty::wxSizeProperty( const wxString& label, const wxString& name,
200     const wxSize& value) : wxPGProperty(label,name)
201 {
202     SetValueI(value);
203     AddPrivateChild( new wxIntProperty(wxT("Width"),wxPG_LABEL,value.x) );
204     AddPrivateChild( new wxIntProperty(wxT("Height"),wxPG_LABEL,value.y) );
205 }
206 
~wxSizeProperty()207 wxSizeProperty::~wxSizeProperty() { }
208 
RefreshChildren()209 void wxSizeProperty::RefreshChildren()
210 {
211     if ( !GetChildCount() ) return;
212     const wxSize& size = wxSizeRefFromVariant(m_value);
213     Item(0)->SetValue( (long)size.x );
214     Item(1)->SetValue( (long)size.y );
215 }
216 
ChildChanged(wxVariant & thisValue,int childIndex,wxVariant & childValue) const217 wxVariant wxSizeProperty::ChildChanged( wxVariant& thisValue,
218                                         int childIndex,
219                                         wxVariant& childValue ) const
220 {
221     wxSize& size = wxSizeRefFromVariant(thisValue);
222     int val = childValue.GetLong();
223     switch ( childIndex )
224     {
225         case 0: size.x = val; break;
226         case 1: size.y = val; break;
227     }
228     wxVariant newVariant;
229     newVariant << size;
230     return newVariant;
231 }
232 
233 // -----------------------------------------------------------------------
234 // wxPointProperty
235 // -----------------------------------------------------------------------
236 
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxPointProperty,wxPGProperty,wxPoint,const wxPoint &,TextCtrl)237 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxPointProperty,wxPGProperty,
238                                wxPoint,const wxPoint&,TextCtrl)
239 
240 wxPointProperty::wxPointProperty( const wxString& label, const wxString& name,
241     const wxPoint& value) : wxPGProperty(label,name)
242 {
243     SetValueI(value);
244     AddPrivateChild( new wxIntProperty(wxT("X"),wxPG_LABEL,value.x) );
245     AddPrivateChild( new wxIntProperty(wxT("Y"),wxPG_LABEL,value.y) );
246 }
247 
~wxPointProperty()248 wxPointProperty::~wxPointProperty() { }
249 
RefreshChildren()250 void wxPointProperty::RefreshChildren()
251 {
252     if ( !GetChildCount() ) return;
253     const wxPoint& point = wxPointRefFromVariant(m_value);
254     Item(0)->SetValue( (long)point.x );
255     Item(1)->SetValue( (long)point.y );
256 }
257 
ChildChanged(wxVariant & thisValue,int childIndex,wxVariant & childValue) const258 wxVariant wxPointProperty::ChildChanged( wxVariant& thisValue,
259                                          int childIndex,
260                                          wxVariant& childValue ) const
261 {
262     wxPoint& point = wxPointRefFromVariant(thisValue);
263     int val = childValue.GetLong();
264     switch ( childIndex )
265     {
266         case 0: point.x = val; break;
267         case 1: point.y = val; break;
268     }
269     wxVariant newVariant;
270     newVariant << point;
271     return newVariant;
272 }
273 
274 
275 // -----------------------------------------------------------------------
276 // Dirs Property
277 // -----------------------------------------------------------------------
278 
279 WX_PG_IMPLEMENT_ARRAYSTRING_PROPERTY_WITH_VALIDATOR(wxDirsProperty, ',',
280                                                     "Browse")
281 
282 #if wxUSE_VALIDATORS
283 
DoGetValidator() const284 wxValidator* wxDirsProperty::DoGetValidator() const
285 {
286     return wxFileProperty::GetClassValidator();
287 }
288 
289 #endif
290 
291 
OnCustomStringEdit(wxWindow * parent,wxString & value)292 bool wxDirsProperty::OnCustomStringEdit( wxWindow* parent, wxString& value )
293 {
294     wxDirDialog dlg(parent,
295                     _("Select a directory to be added to the list:"),
296                     value,
297                     0);
298 
299     if ( dlg.ShowModal() == wxID_OK )
300     {
301         value = dlg.GetPath();
302         return TRUE;
303     }
304     return FALSE;
305 }
306 
307 // -----------------------------------------------------------------------
308 // wxArrayDoubleEditorDialog
309 // -----------------------------------------------------------------------
310 
311 
312 //
313 // You can *almost* convert wxArrayDoubleEditorDialog to wxArrayXXXEditorDialog
314 // by replacing each ArrayDouble with ArrayXXX.
315 //
316 
317 class wxArrayDoubleEditorDialog : public wxPGArrayEditorDialog
318 {
319 public:
320     wxArrayDoubleEditorDialog();
321 
322     void Init();
323 
324     wxArrayDoubleEditorDialog(wxWindow *parent,
325                               const wxString& message,
326                               const wxString& caption,
327                               wxArrayDouble& array,
328                               long style = wxAEDIALOG_STYLE,
329                               const wxPoint& pos = wxDefaultPosition,
330                               const wxSize& sz = wxDefaultSize );
331 
332     bool Create(wxWindow *parent,
333                 const wxString& message,
334                 const wxString& caption,
335                 wxArrayDouble& array,
336                 long style = wxAEDIALOG_STYLE,
337                 const wxPoint& pos = wxDefaultPosition,
338                 const wxSize& sz = wxDefaultSize );
339 
GetArray() const340     const wxArrayDouble& GetArray() const { return m_array; }
341 
342     // Extra method for this type of array
SetPrecision(int precision)343     void SetPrecision ( int precision )
344     {
345         m_precision = precision;
346         m_dtoaTemplate.Empty();
347     }
348 
349 protected:
350     // Mandatory array of type
351     wxArrayDouble   m_array;
352 
353     // Use this to avoid extra wxString creation+Printf
354     // on double-to-wxString conversion.
355     wxString        m_dtoaTemplate;
356 
357     int             m_precision;
358 
359     // Mandatory overridden methods
360     virtual wxString ArrayGet( size_t index );
361     virtual size_t ArrayGetCount();
362     virtual bool ArrayInsert( const wxString& str, int index );
363     virtual bool ArraySet( size_t index, const wxString& str );
364     virtual void ArrayRemoveAt( int index );
365     virtual void ArraySwap( size_t first, size_t second );
366 
367 private:
368     wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxArrayDoubleEditorDialog);
369 };
370 
IMPLEMENT_DYNAMIC_CLASS(wxArrayDoubleEditorDialog,wxPGArrayEditorDialog)371 IMPLEMENT_DYNAMIC_CLASS(wxArrayDoubleEditorDialog, wxPGArrayEditorDialog)
372 
373 //
374 // Array dialog array access and manipulation
375 //
376 
377 wxString wxArrayDoubleEditorDialog::ArrayGet( size_t index )
378 {
379     wxString str;
380     wxPropertyGrid::DoubleToString(str,m_array[index],m_precision,true,&m_dtoaTemplate);
381     return str;
382 }
383 
ArrayGetCount()384 size_t wxArrayDoubleEditorDialog::ArrayGetCount()
385 {
386     return m_array.GetCount();
387 }
388 
ArrayInsert(const wxString & str,int index)389 bool wxArrayDoubleEditorDialog::ArrayInsert( const wxString& str, int index )
390 {
391     double d;
392     if ( !str.ToDouble(&d) )
393         return FALSE;
394 
395     if (index<0)
396         m_array.Add(d);
397     else
398         m_array.Insert(d,index);
399     return TRUE;
400 }
401 
ArraySet(size_t index,const wxString & str)402 bool wxArrayDoubleEditorDialog::ArraySet( size_t index, const wxString& str )
403 {
404     double d;
405     if ( !str.ToDouble(&d) )
406         return FALSE;
407     m_array[index] = d;
408     return TRUE;
409 }
410 
ArrayRemoveAt(int index)411 void wxArrayDoubleEditorDialog::ArrayRemoveAt( int index )
412 {
413     m_array.RemoveAt(index);
414 }
415 
ArraySwap(size_t first,size_t second)416 void wxArrayDoubleEditorDialog::ArraySwap( size_t first, size_t second )
417 {
418     double a = m_array[first];
419     double b = m_array[second];
420     m_array[first] = b;
421     m_array[second] = a;
422 }
423 
424 //
425 // Array dialog construction etc.
426 //
427 
wxArrayDoubleEditorDialog()428 wxArrayDoubleEditorDialog::wxArrayDoubleEditorDialog()
429     : wxPGArrayEditorDialog()
430 {
431     Init();
432 }
433 
Init()434 void wxArrayDoubleEditorDialog::Init()
435 {
436     wxPGArrayEditorDialog::Init();
437     SetPrecision(-1);
438 }
439 
wxArrayDoubleEditorDialog(wxWindow * parent,const wxString & message,const wxString & caption,wxArrayDouble & array,long style,const wxPoint & pos,const wxSize & sz)440 wxArrayDoubleEditorDialog::wxArrayDoubleEditorDialog(wxWindow *parent,
441                               const wxString& message,
442                               const wxString& caption,
443                               wxArrayDouble& array,
444                               long style,
445                               const wxPoint& pos,
446                               const wxSize& sz )
447                               : wxPGArrayEditorDialog()
448 {
449     Init();
450     Create(parent,message,caption,array,style,pos,sz);
451 }
452 
Create(wxWindow * parent,const wxString & message,const wxString & caption,wxArrayDouble & array,long style,const wxPoint & pos,const wxSize & sz)453 bool wxArrayDoubleEditorDialog::Create(wxWindow *parent,
454                 const wxString& message,
455                 const wxString& caption,
456                 wxArrayDouble& array,
457                 long style,
458                 const wxPoint& pos,
459                 const wxSize& sz )
460 {
461 
462     m_array = array;
463 
464     return wxPGArrayEditorDialog::Create (parent,message,caption,style,pos,sz);
465 }
466 
467 // -----------------------------------------------------------------------
468 // wxArrayDoubleProperty
469 // -----------------------------------------------------------------------
470 
471 #include <math.h> // for fabs
472 
473 // Comparison required by value type implementation.
operator ==(const wxArrayDouble & a,const wxArrayDouble & b)474 bool operator == (const wxArrayDouble& a, const wxArrayDouble& b)
475 {
476     if ( a.GetCount() != b.GetCount() )
477         return FALSE;
478 
479     size_t i;
480 
481     for ( i=0; i<a.GetCount(); i++ )
482     {
483         // Can't do direct equality comparison with floating point numbers.
484         if ( fabs(a[i] - b[i]) > 0.0000000001 )
485         {
486             //wxLogDebug(wxT("%f != %f"),a[i],b[i]);
487             return FALSE;
488         }
489     }
490     return TRUE;
491 }
492 
493 WX_PG_IMPLEMENT_VARIANT_DATA_DUMMY_EQ(wxArrayDouble)
494 
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayDoubleProperty,wxPGProperty,wxArrayDouble,const wxArrayDouble &,TextCtrlAndButton)495 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayDoubleProperty,
496                                wxPGProperty,
497                                wxArrayDouble,
498                                const wxArrayDouble&,
499                                TextCtrlAndButton)
500 
501 
502 wxArrayDoubleProperty::wxArrayDoubleProperty (const wxString& label,
503                                                         const wxString& name,
504                                                         const wxArrayDouble& array )
505     : wxPGProperty(label,name)
506 {
507     m_precision = -1;
508 
509     //
510     // Need to figure out delimiter needed for this locale
511     // (ie. can't use comma when comma acts as decimal point in float).
512     wxChar use_delimiter = wxT(',');
513 
514     if (wxString::Format(wxT("%.2f"),12.34).Find(use_delimiter) >= 0)
515         use_delimiter = wxT(';');
516 
517     m_delimiter = use_delimiter;
518 
519     SetValue( WXVARIANT(array) );
520 }
521 
~wxArrayDoubleProperty()522 wxArrayDoubleProperty::~wxArrayDoubleProperty () { }
523 
OnSetValue()524 void wxArrayDoubleProperty::OnSetValue()
525 {
526     // Generate cached display string, to optimize grid drawing
527     GenerateValueAsString( m_display, m_precision, true );
528 }
529 
ValueToString(wxVariant & value,int argFlags) const530 wxString wxArrayDoubleProperty::ValueToString( wxVariant& value,
531                                                int argFlags ) const
532 {
533     wxString s;
534 
535     if ( argFlags & wxPG_FULL_VALUE )
536     {
537         GenerateValueAsString(s,-1,false);
538     }
539     else
540     {
541         //
542         // Display cached string only if value truly matches m_value
543         if ( value.GetData() == m_value.GetData() )
544             return m_display;
545         else
546             GenerateValueAsString( s, m_precision, true );
547     }
548 
549     return s;
550 }
551 
GenerateValueAsString(wxString & target,int prec,bool removeZeroes) const552 void wxArrayDoubleProperty::GenerateValueAsString( wxString& target, int prec, bool removeZeroes ) const
553 {
554     wxString s;
555     wxString template_str;
556     wxChar between[3] = wxT(", ");
557     size_t i;
558 
559     between[0] = m_delimiter;
560 
561     target.Empty();
562 
563     const wxArrayDouble& value = wxArrayDoubleRefFromVariant(m_value);
564 
565     for ( i=0; i<value.GetCount(); i++ )
566     {
567 
568         wxPropertyGrid::DoubleToString(s,value[i],prec,removeZeroes,&template_str);
569 
570         target += s;
571 
572         if ( i<(value.GetCount()-1) )
573             target += between;
574     }
575 }
576 
OnEvent(wxPropertyGrid * propgrid,wxWindow * WXUNUSED (primary),wxEvent & event)577 bool wxArrayDoubleProperty::OnEvent( wxPropertyGrid* propgrid,
578                                      wxWindow* WXUNUSED(primary),
579                                      wxEvent& event)
580 {
581     if ( propgrid->IsMainButtonEvent(event) )
582     {
583         // Update the value in case of last minute changes
584         wxVariant useValue = propgrid->GetUncommittedPropertyValue();
585 
586         wxArrayDouble& value = wxArrayDoubleRefFromVariant(useValue);
587 
588         // Create editor dialog.
589         wxArrayDoubleEditorDialog dlg;
590         dlg.SetPrecision(m_precision);
591         dlg.Create( propgrid, wxEmptyString, m_label, value );
592         dlg.Move( propgrid->GetGoodEditorDialogPosition(this,dlg.GetSize()) );
593 
594         // Execute editor dialog
595         int res = dlg.ShowModal();
596         if ( res == wxID_OK && dlg.IsModified() )
597         {
598             SetValueInEvent( WXVARIANT(dlg.GetArray()) );
599             return true;
600         }
601         return false;
602     }
603     return false;
604 }
605 
StringToValue(wxVariant & variant,const wxString & text,int) const606 bool wxArrayDoubleProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
607 {
608     double tval;
609     wxString tstr;
610     // Add values to a temporary array so that in case
611     // of error we can opt not to use them.
612     wxArrayDouble new_array;
613 
614     bool ok = true;
615 
616     wxChar delimiter = m_delimiter;
617 
618     WX_PG_TOKENIZER1_BEGIN(text,delimiter)
619 
620         if ( !token.empty() )
621         {
622 
623             // If token was invalid, exit the loop now
624             if ( !token.ToDouble(&tval) )
625             {
626                 tstr.Printf ( _("\"%s\" is not a floating-point number."), token.c_str() );
627                 ok = false;
628                 break;
629             }
630             // TODO: Put validator code here
631 
632             new_array.Add(tval);
633 
634         }
635 
636     WX_PG_TOKENIZER1_END()
637 
638     // When invalid token found, show error message and don't change anything
639     if ( !ok )
640     {
641         //ShowError( tstr );
642         return false;
643     }
644 
645     if ( !(wxArrayDoubleRefFromVariant(m_value) == new_array) )
646     {
647         variant = WXVARIANT(new_array);
648         return true;
649     }
650 
651     return false;
652 }
653 
DoSetAttribute(const wxString & name,wxVariant & value)654 bool wxArrayDoubleProperty::DoSetAttribute( const wxString& name, wxVariant& value )
655 {
656     if ( name == wxPG_FLOAT_PRECISION )
657     {
658         m_precision = value.GetLong();
659         GenerateValueAsString( m_display, m_precision, true );
660         return true;
661     }
662     return false;
663 }
664 
665