]> git.saurik.com Git - wxWidgets.git/blob - src/propgrid/props.cpp
27cb58494921d601ba453f063ef05b9e768c3001
[wxWidgets.git] / src / propgrid / props.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/props.cpp
3 // Purpose: Basic Property Classes
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: 2005-05-14
7 // RCS-ID: $Id$
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows licence
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 #if wxUSE_PROPGRID
20
21 #ifndef WX_PRECOMP
22 #include "wx/defs.h"
23 #include "wx/object.h"
24 #include "wx/hash.h"
25 #include "wx/string.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/window.h"
29 #include "wx/panel.h"
30 #include "wx/dc.h"
31 #include "wx/dcclient.h"
32 #include "wx/dcmemory.h"
33 #include "wx/button.h"
34 #include "wx/bmpbuttn.h"
35 #include "wx/pen.h"
36 #include "wx/brush.h"
37 #include "wx/cursor.h"
38 #include "wx/dialog.h"
39 #include "wx/settings.h"
40 #include "wx/msgdlg.h"
41 #include "wx/choice.h"
42 #include "wx/stattext.h"
43 #include "wx/scrolwin.h"
44 #include "wx/dirdlg.h"
45 #include "wx/combobox.h"
46 #include "wx/layout.h"
47 #include "wx/sizer.h"
48 #include "wx/textdlg.h"
49 #include "wx/filedlg.h"
50 #include "wx/intl.h"
51 #endif
52
53 #include "wx/filename.h"
54
55 #include "wx/propgrid/propgrid.h"
56
57 #define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
58
59
60 // -----------------------------------------------------------------------
61 // wxStringProperty
62 // -----------------------------------------------------------------------
63
64 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxStringProperty,wxPGProperty,
65 wxString,const wxString&,TextCtrl)
66
67 wxStringProperty::wxStringProperty( const wxString& label,
68 const wxString& name,
69 const wxString& value )
70 : wxPGProperty(label,name)
71 {
72 SetValue(value);
73 }
74
75 void wxStringProperty::OnSetValue()
76 {
77 if ( !m_value.IsNull() && m_value.GetString() == wxS("<composed>") )
78 SetFlag(wxPG_PROP_COMPOSED_VALUE);
79
80 if ( HasFlag(wxPG_PROP_COMPOSED_VALUE) )
81 {
82 wxString s;
83 DoGenerateComposedValue(s);
84 m_value = s;
85 }
86 }
87
88 wxStringProperty::~wxStringProperty() { }
89
90 wxString wxStringProperty::ValueToString( wxVariant& value,
91 int argFlags ) const
92 {
93 wxString s = value.GetString();
94
95 if ( GetChildCount() && HasFlag(wxPG_PROP_COMPOSED_VALUE) )
96 {
97 // Value stored in m_value is non-editable, non-full value
98 if ( (argFlags & wxPG_FULL_VALUE) || (argFlags & wxPG_EDITABLE_VALUE) )
99 {
100 // Calling this under incorrect conditions will fail
101 wxASSERT_MSG( argFlags & wxPG_VALUE_IS_CURRENT,
102 "Sorry, currently default wxPGProperty::ValueToString() "
103 "implementation only works if value is m_value." );
104
105 DoGenerateComposedValue(s, argFlags);
106 }
107
108 return s;
109 }
110
111 // If string is password and value is for visual purposes,
112 // then return asterisks instead the actual string.
113 if ( (m_flags & wxPG_PROP_PASSWORD) && !(argFlags & (wxPG_FULL_VALUE|wxPG_EDITABLE_VALUE)) )
114 return wxString(wxChar('*'), s.Length());
115
116 return s;
117 }
118
119 bool wxStringProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
120 {
121 if ( GetChildCount() && HasFlag(wxPG_PROP_COMPOSED_VALUE) )
122 return wxPGProperty::StringToValue(variant, text, argFlags);
123
124 if ( variant != text )
125 {
126 variant = text;
127 return true;
128 }
129
130 return false;
131 }
132
133 bool wxStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
134 {
135 if ( name == wxPG_STRING_PASSWORD )
136 {
137 m_flags &= ~(wxPG_PROP_PASSWORD);
138 if ( value.GetLong() ) m_flags |= wxPG_PROP_PASSWORD;
139 RecreateEditor();
140 return false;
141 }
142 return true;
143 }
144
145 // -----------------------------------------------------------------------
146 // wxNumericPropertyValidator
147 // -----------------------------------------------------------------------
148
149 #if wxUSE_VALIDATORS
150
151 wxNumericPropertyValidator::
152 wxNumericPropertyValidator( NumericType numericType, int base )
153 : wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST)
154 {
155 wxArrayString arr;
156 arr.Add(wxS("0"));
157 arr.Add(wxS("1"));
158 arr.Add(wxS("2"));
159 arr.Add(wxS("3"));
160 arr.Add(wxS("4"));
161 arr.Add(wxS("5"));
162 arr.Add(wxS("6"));
163 arr.Add(wxS("7"));
164
165 if ( base >= 10 )
166 {
167 arr.Add(wxS("8"));
168 arr.Add(wxS("9"));
169 if ( base >= 16 )
170 {
171 arr.Add(wxS("a")); arr.Add(wxS("A"));
172 arr.Add(wxS("b")); arr.Add(wxS("B"));
173 arr.Add(wxS("c")); arr.Add(wxS("C"));
174 arr.Add(wxS("d")); arr.Add(wxS("D"));
175 arr.Add(wxS("e")); arr.Add(wxS("E"));
176 arr.Add(wxS("f")); arr.Add(wxS("F"));
177 }
178 }
179
180 if ( numericType == Signed )
181 {
182 arr.Add(wxS("+"));
183 arr.Add(wxS("-"));
184 }
185 else if ( numericType == Float )
186 {
187 arr.Add(wxS("+"));
188 arr.Add(wxS("-"));
189 arr.Add(wxS("."));
190 arr.Add(wxS("e"));
191 }
192
193 SetIncludes(arr);
194 }
195
196 bool wxNumericPropertyValidator::Validate(wxWindow* parent)
197 {
198 if ( !wxTextValidator::Validate(parent) )
199 return false;
200
201 wxWindow* wnd = GetWindow();
202 if ( !wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
203 return true;
204
205 // Do not allow zero-length string
206 wxTextCtrl* tc = static_cast<wxTextCtrl*>(wnd);
207 wxString text = tc->GetValue();
208
209 if ( !text.length() )
210 return false;
211
212 return true;
213 }
214
215 #endif // wxUSE_VALIDATORS
216
217 // -----------------------------------------------------------------------
218 // wxIntProperty
219 // -----------------------------------------------------------------------
220
221 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxIntProperty,wxPGProperty,
222 long,long,TextCtrl)
223
224 wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
225 long value ) : wxPGProperty(label,name)
226 {
227 SetValue(value);
228 }
229
230 wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
231 const wxLongLong& value ) : wxPGProperty(label,name)
232 {
233 SetValue(WXVARIANT(value));
234 }
235
236 wxIntProperty::~wxIntProperty() { }
237
238 wxString wxIntProperty::ValueToString( wxVariant& value,
239 int WXUNUSED(argFlags) ) const
240 {
241 if ( value.GetType() == wxPG_VARIANT_TYPE_LONG )
242 {
243 return wxString::Format(wxS("%li"),value.GetLong());
244 }
245 else if ( value.GetType() == wxPG_VARIANT_TYPE_LONGLONG )
246 {
247 wxLongLong ll = value.GetLongLong();
248 return ll.ToString();
249 }
250
251 return wxEmptyString;
252 }
253
254 bool wxIntProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
255 {
256 wxString s;
257 long value32;
258
259 if ( text.length() == 0 )
260 {
261 variant.MakeNull();
262 return true;
263 }
264
265 // We know it is a number, but let's still check
266 // the return value.
267 if ( text.IsNumber() )
268 {
269 // Remove leading zeroes, so that the number is not interpreted as octal
270 wxString::const_iterator i = text.begin();
271 wxString::const_iterator iMax = text.end() - 1; // Let's allow one, last zero though
272
273 int firstNonZeroPos = 0;
274
275 for ( ; i != iMax; ++i )
276 {
277 wxChar c = *i;
278 if ( c != wxS('0') && c != wxS(' ') )
279 break;
280 firstNonZeroPos++;
281 }
282
283 wxString useText = text.substr(firstNonZeroPos, text.length() - firstNonZeroPos);
284
285 wxString variantType = variant.GetType();
286 bool isPrevLong = variantType == wxPG_VARIANT_TYPE_LONG;
287
288 wxLongLong_t value64 = 0;
289
290 if ( useText.ToLongLong(&value64, 10) &&
291 ( value64 >= INT_MAX || value64 <= INT_MIN )
292 )
293 {
294 bool doChangeValue = isPrevLong;
295
296 if ( !isPrevLong && variantType == wxPG_VARIANT_TYPE_LONGLONG )
297 {
298 wxLongLong oldValue = variant.GetLongLong();
299 if ( oldValue.GetValue() != value64 )
300 doChangeValue = true;
301 }
302
303 if ( doChangeValue )
304 {
305 wxLongLong ll(value64);
306 variant = ll;
307 return true;
308 }
309 }
310
311 if ( useText.ToLong( &value32, 0 ) )
312 {
313 if ( !isPrevLong || variant != value32 )
314 {
315 variant = value32;
316 return true;
317 }
318 }
319 }
320 else if ( argFlags & wxPG_REPORT_ERROR )
321 {
322 }
323 return false;
324 }
325
326 bool wxIntProperty::IntToValue( wxVariant& variant, int value, int WXUNUSED(argFlags) ) const
327 {
328 if ( variant.GetType() != wxPG_VARIANT_TYPE_LONG || variant != (long)value )
329 {
330 variant = (long)value;
331 return true;
332 }
333 return false;
334 }
335
336 //
337 // Common validation code to be called in ValidateValue()
338 // implementations.
339 //
340 // Note that 'value' is reference on purpose, so we can write
341 // back to it when mode is wxPG_PROPERTY_VALIDATION_SATURATE.
342 //
343 template<typename T>
344 bool NumericValidation( const wxPGProperty* property,
345 T& value,
346 wxPGValidationInfo* pValidationInfo,
347 int mode,
348 const wxString& strFmt )
349 {
350 T min = (T) wxINT64_MIN;
351 T max = (T) wxINT64_MAX;
352 wxVariant variant;
353 bool minOk = false;
354 bool maxOk = false;
355
356 variant = property->GetAttribute(wxPGGlobalVars->m_strMin);
357 if ( !variant.IsNull() )
358 {
359 variant.Convert(&min);
360 minOk = true;
361 }
362
363 variant = property->GetAttribute(wxPGGlobalVars->m_strMax);
364 if ( !variant.IsNull() )
365 {
366 variant.Convert(&max);
367 maxOk = true;
368 }
369
370 if ( minOk )
371 {
372 if ( value < min )
373 {
374 if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE )
375 {
376 wxString msg;
377 wxString smin = wxString::Format(strFmt, min);
378 wxString smax = wxString::Format(strFmt, max);
379 if ( !maxOk )
380 msg = wxString::Format(
381 _("Value must be %s or higher."),
382 smin.c_str());
383 else
384 msg = wxString::Format(
385 _("Value must be between %s and %s."),
386 smin.c_str(), smax.c_str());
387 pValidationInfo->SetFailureMessage(msg);
388 }
389 else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE )
390 value = min;
391 else
392 value = max - (min - value);
393 return false;
394 }
395 }
396
397 if ( maxOk )
398 {
399 if ( value > max )
400 {
401 if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE )
402 {
403 wxString msg;
404 wxString smin = wxString::Format(strFmt, min);
405 wxString smax = wxString::Format(strFmt, max);
406 if ( !minOk )
407 msg = wxString::Format(
408 _("Value must be %s or less."),
409 smax.c_str());
410 else
411 msg = wxString::Format(
412 _("Value must be between %s and %s."),
413 smin.c_str(), smax.c_str());
414 pValidationInfo->SetFailureMessage(msg);
415 }
416 else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE )
417 value = max;
418 else
419 value = min + (value - max);
420 return false;
421 }
422 }
423 return true;
424 }
425
426 bool wxIntProperty::DoValidation( const wxPGProperty* property,
427 wxLongLong_t& value,
428 wxPGValidationInfo* pValidationInfo,
429 int mode )
430 {
431 return NumericValidation<wxLongLong_t>(property,
432 value,
433 pValidationInfo,
434 mode,
435 wxS("%lld"));
436 }
437
438 bool wxIntProperty::ValidateValue( wxVariant& value,
439 wxPGValidationInfo& validationInfo ) const
440 {
441 wxLongLong_t ll = value.GetLongLong().GetValue();
442 return DoValidation(this, ll, &validationInfo,
443 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
444 }
445
446 wxValidator* wxIntProperty::GetClassValidator()
447 {
448 #if wxUSE_VALIDATORS
449 WX_PG_DOGETVALIDATOR_ENTRY()
450
451 wxValidator* validator = new wxNumericPropertyValidator(
452 wxNumericPropertyValidator::Signed);
453
454 WX_PG_DOGETVALIDATOR_EXIT(validator)
455 #else
456 return NULL;
457 #endif
458 }
459
460 wxValidator* wxIntProperty::DoGetValidator() const
461 {
462 return GetClassValidator();
463 }
464
465 // -----------------------------------------------------------------------
466 // wxUIntProperty
467 // -----------------------------------------------------------------------
468
469
470 #define wxPG_UINT_TEMPLATE_MAX 8
471
472 static const wxChar* const gs_uintTemplates32[wxPG_UINT_TEMPLATE_MAX] = {
473 wxT("%x"),wxT("0x%x"),wxT("$%x"),
474 wxT("%X"),wxT("0x%X"),wxT("$%X"),
475 wxT("%u"),wxT("%o")
476 };
477
478 static const char* const gs_uintTemplates64[wxPG_UINT_TEMPLATE_MAX] = {
479 "%" wxLongLongFmtSpec "x",
480 "0x%" wxLongLongFmtSpec "x",
481 "$%" wxLongLongFmtSpec "x",
482 "%" wxLongLongFmtSpec "X",
483 "0x%" wxLongLongFmtSpec "X",
484 "$%" wxLongLongFmtSpec "X",
485 "%" wxLongLongFmtSpec "u",
486 "%" wxLongLongFmtSpec "o"
487 };
488
489 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxUIntProperty,wxPGProperty,
490 long,unsigned long,TextCtrl)
491
492 void wxUIntProperty::Init()
493 {
494 m_base = 6; // This is magic number for dec base (must be same as in setattribute)
495 m_realBase = 10;
496 m_prefix = wxPG_PREFIX_NONE;
497 }
498
499 wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
500 unsigned long value ) : wxPGProperty(label,name)
501 {
502 Init();
503 SetValue((long)value);
504 }
505
506 wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
507 const wxULongLong& value ) : wxPGProperty(label,name)
508 {
509 Init();
510 SetValue(WXVARIANT(value));
511 }
512
513 wxUIntProperty::~wxUIntProperty() { }
514
515 wxString wxUIntProperty::ValueToString( wxVariant& value,
516 int WXUNUSED(argFlags) ) const
517 {
518 size_t index = m_base + m_prefix;
519 if ( index >= wxPG_UINT_TEMPLATE_MAX )
520 index = wxPG_BASE_DEC;
521
522 if ( value.GetType() == wxPG_VARIANT_TYPE_LONG )
523 {
524 return wxString::Format(gs_uintTemplates32[index],
525 (unsigned long)value.GetLong());
526 }
527
528 wxULongLong ull = value.GetULongLong();
529
530 return wxString::Format(gs_uintTemplates64[index], ull.GetValue());
531 }
532
533 bool wxUIntProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
534 {
535 wxString variantType = variant.GetType();
536 bool isPrevLong = variantType == wxPG_VARIANT_TYPE_LONG;
537
538 if ( text.length() == 0 )
539 {
540 variant.MakeNull();
541 return true;
542 }
543
544 size_t start = 0;
545 if ( text[0] == wxS('$') )
546 start++;
547
548 wxULongLong_t value64 = 0;
549 wxString s = text.substr(start, text.length() - start);
550
551 if ( s.ToULongLong(&value64, (unsigned int)m_realBase) )
552 {
553 if ( value64 >= LONG_MAX )
554 {
555 bool doChangeValue = isPrevLong;
556
557 if ( !isPrevLong && variantType == wxPG_VARIANT_TYPE_ULONGLONG )
558 {
559 wxULongLong oldValue = variant.GetULongLong();
560 if ( oldValue.GetValue() != value64 )
561 doChangeValue = true;
562 }
563
564 if ( doChangeValue )
565 {
566 variant = wxULongLong(value64);
567 return true;
568 }
569 }
570 else
571 {
572 unsigned long value32 = wxLongLong(value64).GetLo();
573 if ( !isPrevLong || m_value != (long)value32 )
574 {
575 variant = (long)value32;
576 return true;
577 }
578 }
579
580 }
581 return false;
582 }
583
584 bool wxUIntProperty::IntToValue( wxVariant& variant, int number, int WXUNUSED(argFlags) ) const
585 {
586 if ( variant != (long)number )
587 {
588 variant = (long)number;
589 return true;
590 }
591 return false;
592 }
593
594 bool wxUIntProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& validationInfo ) const
595 {
596 wxULongLong_t uul = value.GetULongLong().GetValue();
597 return
598 NumericValidation<wxULongLong_t>(this,
599 uul,
600 &validationInfo,
601 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE,
602 wxS("%llu"));
603 }
604
605 wxValidator* wxUIntProperty::DoGetValidator() const
606 {
607 #if wxUSE_VALIDATORS
608 WX_PG_DOGETVALIDATOR_ENTRY()
609
610 wxValidator* validator = new wxNumericPropertyValidator(
611 wxNumericPropertyValidator::Unsigned,
612 m_realBase);
613
614 WX_PG_DOGETVALIDATOR_EXIT(validator)
615 #else
616 return NULL;
617 #endif
618 }
619
620 bool wxUIntProperty::DoSetAttribute( const wxString& name, wxVariant& value )
621 {
622 if ( name == wxPG_UINT_BASE )
623 {
624 int val = value.GetLong();
625
626 m_realBase = (wxByte) val;
627 if ( m_realBase > 16 )
628 m_realBase = 16;
629
630 //
631 // Translate logical base to a template array index
632 m_base = 7; // oct
633 if ( val == wxPG_BASE_HEX )
634 m_base = 3;
635 else if ( val == wxPG_BASE_DEC )
636 m_base = 6;
637 else if ( val == wxPG_BASE_HEXL )
638 m_base = 0;
639 return true;
640 }
641 else if ( name == wxPG_UINT_PREFIX )
642 {
643 m_prefix = (wxByte) value.GetLong();
644 return true;
645 }
646 return false;
647 }
648
649 // -----------------------------------------------------------------------
650 // wxFloatProperty
651 // -----------------------------------------------------------------------
652
653 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFloatProperty,wxPGProperty,
654 double,double,TextCtrl)
655
656 wxFloatProperty::wxFloatProperty( const wxString& label,
657 const wxString& name,
658 double value )
659 : wxPGProperty(label,name)
660 {
661 m_precision = -1;
662 SetValue(value);
663 }
664
665 wxFloatProperty::~wxFloatProperty() { }
666
667 // This helper method provides standard way for floating point-using
668 // properties to convert values to string.
669 void wxPropertyGrid::DoubleToString(wxString& target,
670 double value,
671 int precision,
672 bool removeZeroes,
673 wxString* precTemplate)
674 {
675 if ( precision >= 0 )
676 {
677 wxString text1;
678 if (!precTemplate)
679 precTemplate = &text1;
680
681 if ( !precTemplate->length() )
682 {
683 *precTemplate = wxS("%.");
684 *precTemplate << wxString::Format( wxS("%i"), precision );
685 *precTemplate << wxS('f');
686 }
687
688 target.Printf( precTemplate->c_str(), value );
689 }
690 else
691 {
692 target.Printf( wxS("%f"), value );
693 }
694
695 if ( removeZeroes && precision != 0 && target.length() )
696 {
697 // Remove excess zeroes (do not remove this code just yet,
698 // since sprintf can't do the same consistently across platforms).
699 wxString::const_iterator i = target.end() - 1;
700 size_t new_len = target.length() - 1;
701
702 for ( ; i != target.begin(); --i )
703 {
704 if ( *i != wxS('0') )
705 break;
706 new_len--;
707 }
708
709 wxChar cur_char = *i;
710 if ( cur_char != wxS('.') && cur_char != wxS(',') )
711 new_len++;
712
713 if ( new_len != target.length() )
714 target.resize(new_len);
715 }
716
717 // Remove sign from zero
718 if ( target.length() >= 2 && target[0] == wxS('-') )
719 {
720 bool isZero = true;
721 wxString::const_iterator i = target.begin() + 1;
722
723 for ( ; i != target.end(); i++ )
724 {
725 if ( *i != wxS('0') && *i != wxS('.') )
726 {
727 isZero = false;
728 break;
729 }
730 }
731
732 if ( isZero )
733 target.erase(target.begin());
734 }
735 }
736
737 wxString wxFloatProperty::ValueToString( wxVariant& value,
738 int argFlags ) const
739 {
740 wxString text;
741 if ( !value.IsNull() )
742 {
743 wxPropertyGrid::DoubleToString(text,
744 value,
745 m_precision,
746 !(argFlags & wxPG_FULL_VALUE),
747 NULL);
748 }
749 return text;
750 }
751
752 bool wxFloatProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
753 {
754 wxString s;
755 double value;
756
757 if ( text.length() == 0 )
758 {
759 variant.MakeNull();
760 return true;
761 }
762
763 bool res = text.ToDouble(&value);
764 if ( res )
765 {
766 if ( variant != value )
767 {
768 variant = value;
769 return true;
770 }
771 }
772 else if ( argFlags & wxPG_REPORT_ERROR )
773 {
774 }
775 return false;
776 }
777
778 bool wxFloatProperty::DoValidation( const wxPGProperty* property,
779 double& value,
780 wxPGValidationInfo* pValidationInfo,
781 int mode )
782 {
783 return NumericValidation<double>(property,
784 value,
785 pValidationInfo,
786 mode,
787 wxS("%g"));
788 }
789
790 bool
791 wxFloatProperty::ValidateValue( wxVariant& value,
792 wxPGValidationInfo& validationInfo ) const
793 {
794 double fpv = value.GetDouble();
795 return DoValidation(this, fpv, &validationInfo,
796 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
797 }
798
799 bool wxFloatProperty::DoSetAttribute( const wxString& name, wxVariant& value )
800 {
801 if ( name == wxPG_FLOAT_PRECISION )
802 {
803 m_precision = value.GetLong();
804 return true;
805 }
806 return false;
807 }
808
809 wxValidator*
810 wxFloatProperty::GetClassValidator()
811 {
812 #if wxUSE_VALIDATORS
813 WX_PG_DOGETVALIDATOR_ENTRY()
814
815 wxValidator* validator = new wxNumericPropertyValidator(
816 wxNumericPropertyValidator::Float);
817
818 WX_PG_DOGETVALIDATOR_EXIT(validator)
819 #else
820 return NULL;
821 #endif
822 }
823
824 wxValidator* wxFloatProperty::DoGetValidator() const
825 {
826 return GetClassValidator();
827 }
828
829 // -----------------------------------------------------------------------
830 // wxBoolProperty
831 // -----------------------------------------------------------------------
832
833 // We cannot use standard WX_PG_IMPLEMENT_PROPERTY_CLASS macro, since
834 // there is a custom GetEditorClass.
835
836 IMPLEMENT_DYNAMIC_CLASS(wxBoolProperty, wxPGProperty)
837
838 const wxPGEditor* wxBoolProperty::DoGetEditorClass() const
839 {
840 // Select correct editor control.
841 #if wxPG_INCLUDE_CHECKBOX
842 if ( !(m_flags & wxPG_PROP_USE_CHECKBOX) )
843 return wxPGEditor_Choice;
844 return wxPGEditor_CheckBox;
845 #else
846 return wxPGEditor_Choice;
847 #endif
848 }
849
850 wxBoolProperty::wxBoolProperty( const wxString& label, const wxString& name, bool value ) :
851 wxPGProperty(label,name)
852 {
853 m_choices.Assign(wxPGGlobalVars->m_boolChoices);
854
855 SetValue(wxPGVariant_Bool(value));
856
857 m_flags |= wxPG_PROP_USE_DCC;
858 }
859
860 wxBoolProperty::~wxBoolProperty() { }
861
862 wxString wxBoolProperty::ValueToString( wxVariant& value,
863 int argFlags ) const
864 {
865 bool boolValue = value.GetBool();
866
867 // As a fragment of composite string value,
868 // make it a little more readable.
869 if ( argFlags & wxPG_COMPOSITE_FRAGMENT )
870 {
871 if ( boolValue )
872 {
873 return m_label;
874 }
875 else
876 {
877 if ( argFlags & wxPG_UNEDITABLE_COMPOSITE_FRAGMENT )
878 return wxEmptyString;
879
880 wxString notFmt;
881 if ( wxPGGlobalVars->m_autoGetTranslation )
882 notFmt = _("Not %s");
883 else
884 notFmt = wxS("Not %s");
885
886 return wxString::Format(notFmt.c_str(), m_label.c_str());
887 }
888 }
889
890 if ( !(argFlags & wxPG_FULL_VALUE) )
891 {
892 return wxPGGlobalVars->m_boolChoices[boolValue?1:0].GetText();
893 }
894
895 wxString text;
896
897 if ( boolValue ) text = wxS("true");
898 else text = wxS("false");
899
900 return text;
901 }
902
903 bool wxBoolProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
904 {
905 bool boolValue = false;
906 if ( text.CmpNoCase(wxPGGlobalVars->m_boolChoices[1].GetText()) == 0 ||
907 text.CmpNoCase(wxS("true")) == 0 ||
908 text.CmpNoCase(m_label) == 0 )
909 boolValue = true;
910
911 if ( text.length() == 0 )
912 {
913 variant.MakeNull();
914 return true;
915 }
916
917 if ( variant != boolValue )
918 {
919 variant = wxPGVariant_Bool(boolValue);
920 return true;
921 }
922 return false;
923 }
924
925 bool wxBoolProperty::IntToValue( wxVariant& variant, int value, int ) const
926 {
927 bool boolValue = value ? true : false;
928
929 if ( variant != boolValue )
930 {
931 variant = wxPGVariant_Bool(boolValue);
932 return true;
933 }
934 return false;
935 }
936
937 bool wxBoolProperty::DoSetAttribute( const wxString& name, wxVariant& value )
938 {
939 #if wxPG_INCLUDE_CHECKBOX
940 if ( name == wxPG_BOOL_USE_CHECKBOX )
941 {
942 if ( value.GetLong() )
943 m_flags |= wxPG_PROP_USE_CHECKBOX;
944 else
945 m_flags &= ~(wxPG_PROP_USE_CHECKBOX);
946 return true;
947 }
948 #endif
949 if ( name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
950 {
951 if ( value.GetLong() )
952 m_flags |= wxPG_PROP_USE_DCC;
953 else
954 m_flags &= ~(wxPG_PROP_USE_DCC);
955 return true;
956 }
957 return false;
958 }
959
960 // -----------------------------------------------------------------------
961 // wxEnumProperty
962 // -----------------------------------------------------------------------
963
964 IMPLEMENT_DYNAMIC_CLASS(wxEnumProperty, wxPGProperty)
965
966 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEnumProperty,long,Choice)
967
968 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
969 const long* values, int value ) : wxPGProperty(label,name)
970 {
971 SetIndex(0);
972
973 if ( labels )
974 {
975 m_choices.Add(labels,values);
976
977 if ( GetItemCount() )
978 SetValue( (long)value );
979 }
980 }
981
982 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
983 const long* values, wxPGChoices* choicesCache, int value )
984 : wxPGProperty(label,name)
985 {
986 SetIndex(0);
987
988 wxASSERT( choicesCache );
989
990 if ( choicesCache->IsOk() )
991 {
992 m_choices.Assign( *choicesCache );
993 m_value = wxPGVariant_Zero;
994 }
995 else if ( labels )
996 {
997 m_choices.Add(labels,values);
998
999 if ( GetItemCount() )
1000 SetValue( (long)value );
1001 }
1002 }
1003
1004 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
1005 const wxArrayString& labels, const wxArrayInt& values, int value )
1006 : wxPGProperty(label,name)
1007 {
1008 SetIndex(0);
1009
1010 if ( &labels && labels.size() )
1011 {
1012 m_choices.Set(labels, values);
1013
1014 if ( GetItemCount() )
1015 SetValue( (long)value );
1016 }
1017 }
1018
1019 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
1020 wxPGChoices& choices, int value )
1021 : wxPGProperty(label,name)
1022 {
1023 m_choices.Assign( choices );
1024
1025 if ( GetItemCount() )
1026 SetValue( (long)value );
1027 }
1028
1029 int wxEnumProperty::GetIndexForValue( int value ) const
1030 {
1031 if ( !m_choices.IsOk() )
1032 return -1;
1033
1034 int intVal = m_choices.Index(value);
1035 if ( intVal >= 0 )
1036 return intVal;
1037
1038 return value;
1039 }
1040
1041 wxEnumProperty::~wxEnumProperty ()
1042 {
1043 }
1044
1045 int wxEnumProperty::ms_nextIndex = -2;
1046
1047 void wxEnumProperty::OnSetValue()
1048 {
1049 wxString variantType = m_value.GetType();
1050
1051 if ( variantType == wxPG_VARIANT_TYPE_LONG )
1052 {
1053 ValueFromInt_( m_value, m_value.GetLong(), wxPG_FULL_VALUE );
1054 }
1055 else if ( variantType == wxPG_VARIANT_TYPE_STRING )
1056 {
1057 ValueFromString_( m_value, m_value.GetString(), 0 );
1058 }
1059 else
1060 {
1061 wxFAIL;
1062 }
1063
1064 if ( ms_nextIndex != -2 )
1065 {
1066 m_index = ms_nextIndex;
1067 ms_nextIndex = -2;
1068 }
1069 }
1070
1071 bool wxEnumProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& WXUNUSED(validationInfo) ) const
1072 {
1073 // Make sure string value is in the list,
1074 // unless property has string as preferred value type
1075 // To reduce code size, use conversion here as well
1076 if ( value.GetType() == wxPG_VARIANT_TYPE_STRING &&
1077 !this->IsKindOf(CLASSINFO(wxEditEnumProperty)) )
1078 return ValueFromString_( value, value.GetString(), wxPG_PROPERTY_SPECIFIC );
1079
1080 return true;
1081 }
1082
1083 wxString wxEnumProperty::ValueToString( wxVariant& value,
1084 int WXUNUSED(argFlags) ) const
1085 {
1086 if ( value.GetType() == wxPG_VARIANT_TYPE_STRING )
1087 return value.GetString();
1088
1089 int index = m_choices.Index(value.GetLong());
1090 if ( index < 0 )
1091 return wxEmptyString;
1092
1093 return m_choices.GetLabel(index);
1094 }
1095
1096 bool wxEnumProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1097 {
1098 return ValueFromString_( variant, text, argFlags );
1099 }
1100
1101 bool wxEnumProperty::IntToValue( wxVariant& variant, int intVal, int argFlags ) const
1102 {
1103 return ValueFromInt_( variant, intVal, argFlags );
1104 }
1105
1106 bool wxEnumProperty::ValueFromString_( wxVariant& value, const wxString& text, int argFlags ) const
1107 {
1108 int useIndex = -1;
1109 long useValue = 0;
1110
1111 for ( unsigned int i=0; i<m_choices.GetCount(); i++ )
1112 {
1113 const wxString& entryLabel = m_choices.GetLabel(i);
1114 if ( text.CmpNoCase(entryLabel) == 0 )
1115 {
1116 useIndex = (int)i;
1117 useValue = m_choices.GetValue(i);
1118 break;
1119 }
1120 }
1121
1122 bool asText = false;
1123
1124 bool isEdit = this->IsKindOf(CLASSINFO(wxEditEnumProperty));
1125
1126 // If text not any of the choices, store as text instead
1127 // (but only if we are wxEditEnumProperty)
1128 if ( useIndex == -1 && isEdit )
1129 {
1130 asText = true;
1131 }
1132
1133 int setAsNextIndex = -2;
1134
1135 if ( asText )
1136 {
1137 setAsNextIndex = -1;
1138 value = text;
1139 }
1140 else if ( useIndex != GetIndex() )
1141 {
1142 if ( useIndex != -1 )
1143 {
1144 setAsNextIndex = useIndex;
1145 value = (long)useValue;
1146 }
1147 else
1148 {
1149 setAsNextIndex = -1;
1150 value = wxPGVariant_MinusOne;
1151 }
1152 }
1153
1154 if ( setAsNextIndex != -2 )
1155 {
1156 // If wxPG_PROPERTY_SPECIFIC is set, then this is done for
1157 // validation purposes only, and index must not be changed
1158 if ( !(argFlags & wxPG_PROPERTY_SPECIFIC) )
1159 ms_nextIndex = setAsNextIndex;
1160
1161 if ( isEdit || setAsNextIndex != -1 )
1162 return true;
1163 else
1164 return false;
1165 }
1166 return false;
1167 }
1168
1169 bool wxEnumProperty::ValueFromInt_( wxVariant& variant, int intVal, int argFlags ) const
1170 {
1171 // If wxPG_FULL_VALUE is *not* in argFlags, then intVal is index from combo box.
1172 //
1173 ms_nextIndex = -2;
1174
1175 if ( argFlags & wxPG_FULL_VALUE )
1176 {
1177 ms_nextIndex = GetIndexForValue( intVal );
1178 }
1179 else
1180 {
1181 if ( intVal != GetIndex() )
1182 {
1183 ms_nextIndex = intVal;
1184 }
1185 }
1186
1187 if ( ms_nextIndex != -2 )
1188 {
1189 if ( !(argFlags & wxPG_FULL_VALUE) )
1190 intVal = m_choices.GetValue(intVal);
1191
1192 variant = (long)intVal;
1193
1194 return true;
1195 }
1196
1197 return false;
1198 }
1199
1200 void
1201 wxEnumProperty::OnValidationFailure( wxVariant& WXUNUSED(pendingValue) )
1202 {
1203 // Revert index
1204 ResetNextIndex();
1205 }
1206
1207 void wxEnumProperty::SetIndex( int index )
1208 {
1209 ms_nextIndex = -2;
1210 m_index = index;
1211 }
1212
1213 int wxEnumProperty::GetIndex() const
1214 {
1215 if ( m_value.IsNull() )
1216 return -1;
1217
1218 if ( ms_nextIndex != -2 )
1219 return ms_nextIndex;
1220
1221 return m_index;
1222 }
1223
1224 // -----------------------------------------------------------------------
1225 // wxEditEnumProperty
1226 // -----------------------------------------------------------------------
1227
1228 IMPLEMENT_DYNAMIC_CLASS(wxEditEnumProperty, wxPGProperty)
1229
1230 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEditEnumProperty,wxString,ComboBox)
1231
1232 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
1233 const long* values, const wxString& value )
1234 : wxEnumProperty(label,name,labels,values,0)
1235 {
1236 SetValue( value );
1237 }
1238
1239 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
1240 const long* values, wxPGChoices* choicesCache, const wxString& value )
1241 : wxEnumProperty(label,name,labels,values,choicesCache,0)
1242 {
1243 SetValue( value );
1244 }
1245
1246 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
1247 const wxArrayString& labels, const wxArrayInt& values, const wxString& value )
1248 : wxEnumProperty(label,name,labels,values,0)
1249 {
1250 SetValue( value );
1251 }
1252
1253 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
1254 wxPGChoices& choices, const wxString& value )
1255 : wxEnumProperty(label,name,choices,0)
1256 {
1257 SetValue( value );
1258 }
1259
1260 wxEditEnumProperty::~wxEditEnumProperty()
1261 {
1262 }
1263
1264 // -----------------------------------------------------------------------
1265 // wxFlagsProperty
1266 // -----------------------------------------------------------------------
1267
1268 IMPLEMENT_DYNAMIC_CLASS(wxFlagsProperty,wxPGProperty)
1269
1270 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxFlagsProperty,long,TextCtrl)
1271
1272 void wxFlagsProperty::Init()
1273 {
1274 long value = m_value;
1275
1276 //
1277 // Generate children
1278 //
1279 unsigned int i;
1280
1281 unsigned int prevChildCount = m_children.size();
1282
1283 int oldSel = -1;
1284 if ( prevChildCount )
1285 {
1286 wxPropertyGridPageState* state = GetParentState();
1287
1288 // State safety check (it may be NULL in immediate parent)
1289 wxASSERT( state );
1290
1291 if ( state )
1292 {
1293 wxPGProperty* selected = state->GetSelection();
1294 if ( selected )
1295 {
1296 if ( selected->GetParent() == this )
1297 oldSel = selected->GetIndexInParent();
1298 else if ( selected == this )
1299 oldSel = -2;
1300 }
1301 }
1302 state->DoClearSelection();
1303 }
1304
1305 // Delete old children
1306 for ( i=0; i<prevChildCount; i++ )
1307 delete m_children[i];
1308
1309 m_children.clear();
1310
1311 // Relay wxPG_BOOL_USE_CHECKBOX and wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING
1312 // to child bool property controls.
1313 long attrUseCheckBox = GetAttributeAsLong(wxPG_BOOL_USE_CHECKBOX, 0);
1314 long attrUseDCC = GetAttributeAsLong(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
1315 0);
1316
1317 if ( m_choices.IsOk() )
1318 {
1319 const wxPGChoices& choices = m_choices;
1320
1321 for ( i=0; i<GetItemCount(); i++ )
1322 {
1323 bool child_val;
1324 child_val = ( value & choices.GetValue(i) )?true:false;
1325
1326 wxPGProperty* boolProp;
1327 wxString label = GetLabel(i);
1328
1329 #if wxUSE_INTL
1330 if ( wxPGGlobalVars->m_autoGetTranslation )
1331 {
1332 boolProp = new wxBoolProperty( ::wxGetTranslation(label), label, child_val );
1333 }
1334 else
1335 #endif
1336 {
1337 boolProp = new wxBoolProperty( label, label, child_val );
1338 }
1339 if ( attrUseCheckBox )
1340 boolProp->SetAttribute(wxPG_BOOL_USE_CHECKBOX,
1341 true);
1342 if ( attrUseDCC )
1343 boolProp->SetAttribute(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
1344 true);
1345 AddPrivateChild(boolProp);
1346 }
1347
1348 m_oldChoicesData = m_choices.GetDataPtr();
1349 }
1350
1351 m_oldValue = m_value;
1352
1353 if ( prevChildCount )
1354 SubPropsChanged(oldSel);
1355 }
1356
1357 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1358 const wxChar* const* labels, const long* values, long value ) : wxPGProperty(label,name)
1359 {
1360 m_oldChoicesData = NULL;
1361
1362 if ( labels )
1363 {
1364 m_choices.Set(labels,values);
1365
1366 wxASSERT( GetItemCount() );
1367
1368 SetValue( value );
1369 }
1370 else
1371 {
1372 m_value = wxPGVariant_Zero;
1373 }
1374 }
1375
1376 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1377 const wxArrayString& labels, const wxArrayInt& values, int value )
1378 : wxPGProperty(label,name)
1379 {
1380 m_oldChoicesData = NULL;
1381
1382 if ( &labels && labels.size() )
1383 {
1384 m_choices.Set(labels,values);
1385
1386 wxASSERT( GetItemCount() );
1387
1388 SetValue( (long)value );
1389 }
1390 else
1391 {
1392 m_value = wxPGVariant_Zero;
1393 }
1394 }
1395
1396 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1397 wxPGChoices& choices, long value )
1398 : wxPGProperty(label,name)
1399 {
1400 m_oldChoicesData = NULL;
1401
1402 if ( choices.IsOk() )
1403 {
1404 m_choices.Assign(choices);
1405
1406 wxASSERT( GetItemCount() );
1407
1408 SetValue( value );
1409 }
1410 else
1411 {
1412 m_value = wxPGVariant_Zero;
1413 }
1414 }
1415
1416 wxFlagsProperty::~wxFlagsProperty()
1417 {
1418 }
1419
1420 void wxFlagsProperty::OnSetValue()
1421 {
1422 if ( !m_choices.IsOk() || !GetItemCount() )
1423 {
1424 m_value = wxPGVariant_Zero;
1425 }
1426 else
1427 {
1428 long val = m_value.GetLong();
1429
1430 long fullFlags = 0;
1431
1432 // normalize the value (i.e. remove extra flags)
1433 unsigned int i;
1434 const wxPGChoices& choices = m_choices;
1435 for ( i = 0; i < GetItemCount(); i++ )
1436 {
1437 fullFlags |= choices.GetValue(i);
1438 }
1439
1440 val &= fullFlags;
1441
1442 m_value = val;
1443
1444 // Need to (re)init now?
1445 if ( GetChildCount() != GetItemCount() ||
1446 m_choices.GetDataPtr() != m_oldChoicesData )
1447 {
1448 Init();
1449 }
1450 }
1451
1452 long newFlags = m_value;
1453
1454 if ( newFlags != m_oldValue )
1455 {
1456 // Set child modified states
1457 unsigned int i;
1458 const wxPGChoices& choices = m_choices;
1459 for ( i = 0; i<GetItemCount(); i++ )
1460 {
1461 int flag;
1462
1463 flag = choices.GetValue(i);
1464
1465 if ( (newFlags & flag) != (m_oldValue & flag) )
1466 Item(i)->ChangeFlag( wxPG_PROP_MODIFIED, true );
1467 }
1468
1469 m_oldValue = newFlags;
1470 }
1471 }
1472
1473 wxString wxFlagsProperty::ValueToString( wxVariant& value,
1474 int WXUNUSED(argFlags) ) const
1475 {
1476 wxString text;
1477
1478 if ( !m_choices.IsOk() )
1479 return text;
1480
1481 long flags = value;
1482 unsigned int i;
1483 const wxPGChoices& choices = m_choices;
1484
1485 for ( i = 0; i < GetItemCount(); i++ )
1486 {
1487 int doAdd;
1488 doAdd = ( flags & choices.GetValue(i) );
1489
1490 if ( doAdd )
1491 {
1492 text += choices.GetLabel(i);
1493 text += wxS(", ");
1494 }
1495 }
1496
1497 // remove last comma
1498 if ( text.Len() > 1 )
1499 text.Truncate ( text.Len() - 2 );
1500
1501 return text;
1502 }
1503
1504 // Translate string into flag tokens
1505 bool wxFlagsProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
1506 {
1507 if ( !m_choices.IsOk() )
1508 return false;
1509
1510 long newFlags = 0;
1511
1512 // semicolons are no longer valid delimeters
1513 WX_PG_TOKENIZER1_BEGIN(text,wxS(','))
1514
1515 if ( token.length() )
1516 {
1517 // Determine which one it is
1518 long bit = IdToBit( token );
1519
1520 if ( bit != -1 )
1521 {
1522 // Changed?
1523 newFlags |= bit;
1524 }
1525 else
1526 {
1527 break;
1528 }
1529 }
1530
1531 WX_PG_TOKENIZER1_END()
1532
1533 if ( variant != (long)newFlags )
1534 {
1535 variant = (long)newFlags;
1536 return true;
1537 }
1538
1539 return false;
1540 }
1541
1542 // Converts string id to a relevant bit.
1543 long wxFlagsProperty::IdToBit( const wxString& id ) const
1544 {
1545 unsigned int i;
1546 for ( i = 0; i < GetItemCount(); i++ )
1547 {
1548 if ( id == GetLabel(i) )
1549 {
1550 return m_choices.GetValue(i);
1551 }
1552 }
1553 return -1;
1554 }
1555
1556 void wxFlagsProperty::RefreshChildren()
1557 {
1558 if ( !m_choices.IsOk() || !GetChildCount() ) return;
1559
1560 int flags = m_value.GetLong();
1561
1562 const wxPGChoices& choices = m_choices;
1563 unsigned int i;
1564 for ( i = 0; i < GetItemCount(); i++ )
1565 {
1566 long flag;
1567
1568 flag = choices.GetValue(i);
1569
1570 long subVal = flags & flag;
1571 wxPGProperty* p = Item(i);
1572
1573 if ( subVal != (m_oldValue & flag) )
1574 p->ChangeFlag( wxPG_PROP_MODIFIED, true );
1575
1576 p->SetValue( subVal?true:false );
1577 }
1578
1579 m_oldValue = flags;
1580 }
1581
1582 wxVariant wxFlagsProperty::ChildChanged( wxVariant& thisValue,
1583 int childIndex,
1584 wxVariant& childValue ) const
1585 {
1586 long oldValue = thisValue.GetLong();
1587 long val = childValue.GetLong();
1588 unsigned long vi = m_choices.GetValue(childIndex);
1589
1590 if ( val )
1591 return (long) (oldValue | vi);
1592
1593 return (long) (oldValue & ~(vi));
1594 }
1595
1596 bool wxFlagsProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1597 {
1598 if ( name == wxPG_BOOL_USE_CHECKBOX ||
1599 name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
1600 {
1601 for ( size_t i=0; i<GetChildCount(); i++ )
1602 {
1603 Item(i)->SetAttribute(name, value);
1604 }
1605 // Must return false so that the attribute is stored in
1606 // flag property's actual property storage
1607 return false;
1608 }
1609 return false;
1610 }
1611
1612 // -----------------------------------------------------------------------
1613 // wxDirProperty
1614 // -----------------------------------------------------------------------
1615
1616 IMPLEMENT_DYNAMIC_CLASS(wxDirProperty, wxLongStringProperty)
1617
1618 wxDirProperty::wxDirProperty( const wxString& name, const wxString& label, const wxString& value )
1619 : wxLongStringProperty(name,label,value)
1620 {
1621 m_flags |= wxPG_PROP_NO_ESCAPE;
1622 }
1623
1624 wxDirProperty::~wxDirProperty() { }
1625
1626 wxValidator* wxDirProperty::DoGetValidator() const
1627 {
1628 return wxFileProperty::GetClassValidator();
1629 }
1630
1631 bool wxDirProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
1632 {
1633 // Update property value from editor, if necessary
1634 wxSize dlg_sz(300,400);
1635
1636 wxString dlgMessage(m_dlgMessage);
1637 if ( dlgMessage.empty() )
1638 dlgMessage = _("Choose a directory:");
1639 wxDirDialog dlg( propGrid,
1640 dlgMessage,
1641 value,
1642 0,
1643 #if !wxPG_SMALL_SCREEN
1644 propGrid->GetGoodEditorDialogPosition(this,dlg_sz),
1645 dlg_sz
1646 #else
1647 wxDefaultPosition,
1648 wxDefaultSize
1649 #endif
1650 );
1651
1652 if ( dlg.ShowModal() == wxID_OK )
1653 {
1654 value = dlg.GetPath();
1655 return true;
1656 }
1657 return false;
1658 }
1659
1660 bool wxDirProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1661 {
1662 if ( name == wxPG_DIR_DIALOG_MESSAGE )
1663 {
1664 m_dlgMessage = value.GetString();
1665 return true;
1666 }
1667 return false;
1668 }
1669
1670 // -----------------------------------------------------------------------
1671 // wxPGFileDialogAdapter
1672 // -----------------------------------------------------------------------
1673
1674 bool wxPGFileDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1675 {
1676 wxFileProperty* fileProp = NULL;
1677 wxString path;
1678 int indFilter = -1;
1679
1680 if ( property->IsKindOf(CLASSINFO(wxFileProperty)) )
1681 {
1682 fileProp = ((wxFileProperty*)property);
1683 wxFileName filename = fileProp->GetValue().GetString();
1684 path = filename.GetPath();
1685 indFilter = fileProp->m_indFilter;
1686
1687 if ( !path.length() && fileProp->m_basePath.length() )
1688 path = fileProp->m_basePath;
1689 }
1690 else
1691 {
1692 wxFileName fn(property->GetValue().GetString());
1693 path = fn.GetPath();
1694 }
1695
1696 wxFileDialog dlg( propGrid->GetPanel(),
1697 property->GetAttribute(wxS("DialogTitle"), _("Choose a file")),
1698 property->GetAttribute(wxS("InitialPath"), path),
1699 wxEmptyString,
1700 property->GetAttribute(wxPG_FILE_WILDCARD, _("All files (*.*)|*.*")),
1701 0,
1702 wxDefaultPosition );
1703
1704 if ( indFilter >= 0 )
1705 dlg.SetFilterIndex( indFilter );
1706
1707 if ( dlg.ShowModal() == wxID_OK )
1708 {
1709 if ( fileProp )
1710 fileProp->m_indFilter = dlg.GetFilterIndex();
1711 SetValue( dlg.GetPath() );
1712 return true;
1713 }
1714 return false;
1715 }
1716
1717 // -----------------------------------------------------------------------
1718 // wxFileProperty
1719 // -----------------------------------------------------------------------
1720
1721 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFileProperty,wxPGProperty,
1722 wxString,const wxString&,TextCtrlAndButton)
1723
1724 wxFileProperty::wxFileProperty( const wxString& label, const wxString& name,
1725 const wxString& value ) : wxPGProperty(label,name)
1726 {
1727 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1728 m_indFilter = -1;
1729 SetAttribute( wxPG_FILE_WILDCARD, _("All files (*.*)|*.*") );
1730
1731 SetValue(value);
1732 }
1733
1734 wxFileProperty::~wxFileProperty() {}
1735
1736 wxValidator* wxFileProperty::GetClassValidator()
1737 {
1738 #if wxUSE_VALIDATORS
1739 WX_PG_DOGETVALIDATOR_ENTRY()
1740
1741 // Atleast wxPython 2.6.2.1 required that the string argument is given
1742 static wxString v;
1743 wxTextValidator* validator = new wxTextValidator(wxFILTER_EXCLUDE_CHAR_LIST,&v);
1744
1745 wxArrayString exChars;
1746 exChars.Add(wxS("?"));
1747 exChars.Add(wxS("*"));
1748 exChars.Add(wxS("|"));
1749 exChars.Add(wxS("<"));
1750 exChars.Add(wxS(">"));
1751 exChars.Add(wxS("\""));
1752
1753 validator->SetExcludes(exChars);
1754
1755 WX_PG_DOGETVALIDATOR_EXIT(validator)
1756 #else
1757 return NULL;
1758 #endif
1759 }
1760
1761 wxValidator* wxFileProperty::DoGetValidator() const
1762 {
1763 return GetClassValidator();
1764 }
1765
1766 void wxFileProperty::OnSetValue()
1767 {
1768 const wxString& fnstr = m_value.GetString();
1769
1770 wxFileName filename = fnstr;
1771
1772 if ( !filename.HasName() )
1773 {
1774 m_value = wxPGVariant_EmptyString;
1775 }
1776
1777 // Find index for extension.
1778 if ( m_indFilter < 0 && fnstr.length() )
1779 {
1780 wxString ext = filename.GetExt();
1781 int curind = 0;
1782 size_t pos = 0;
1783 size_t len = m_wildcard.length();
1784
1785 pos = m_wildcard.find(wxS("|"), pos);
1786 while ( pos != wxString::npos && pos < (len-3) )
1787 {
1788 size_t ext_begin = pos + 3;
1789
1790 pos = m_wildcard.find(wxS("|"), ext_begin);
1791 if ( pos == wxString::npos )
1792 pos = len;
1793 wxString found_ext = m_wildcard.substr(ext_begin, pos-ext_begin);
1794
1795 if ( found_ext.length() > 0 )
1796 {
1797 if ( found_ext[0] == wxS('*') )
1798 {
1799 m_indFilter = curind;
1800 break;
1801 }
1802 if ( ext.CmpNoCase(found_ext) == 0 )
1803 {
1804 m_indFilter = curind;
1805 break;
1806 }
1807 }
1808
1809 if ( pos != len )
1810 pos = m_wildcard.find(wxS("|"), pos+1);
1811
1812 curind++;
1813 }
1814 }
1815 }
1816
1817 wxFileName wxFileProperty::GetFileName() const
1818 {
1819 wxFileName filename;
1820
1821 if ( !m_value.IsNull() )
1822 filename = m_value.GetString();
1823
1824 return filename;
1825 }
1826
1827 wxString wxFileProperty::ValueToString( wxVariant& value,
1828 int argFlags ) const
1829 {
1830 wxFileName filename = value.GetString();
1831
1832 if ( !filename.HasName() )
1833 return wxEmptyString;
1834
1835 wxString fullName = filename.GetFullName();
1836 if ( !fullName.length() )
1837 return wxEmptyString;
1838
1839 if ( argFlags & wxPG_FULL_VALUE )
1840 {
1841 return filename.GetFullPath();
1842 }
1843 else if ( m_flags & wxPG_PROP_SHOW_FULL_FILENAME )
1844 {
1845 if ( m_basePath.Length() )
1846 {
1847 wxFileName fn2(filename);
1848 fn2.MakeRelativeTo(m_basePath);
1849 return fn2.GetFullPath();
1850 }
1851 return filename.GetFullPath();
1852 }
1853
1854 return filename.GetFullName();
1855 }
1856
1857 wxPGEditorDialogAdapter* wxFileProperty::GetEditorDialog() const
1858 {
1859 return new wxPGFileDialogAdapter();
1860 }
1861
1862 bool wxFileProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1863 {
1864 wxFileName filename = variant.GetString();
1865
1866 if ( (m_flags & wxPG_PROP_SHOW_FULL_FILENAME) || (argFlags & wxPG_FULL_VALUE) )
1867 {
1868 if ( filename != text )
1869 {
1870 variant = text;
1871 return true;
1872 }
1873 }
1874 else
1875 {
1876 if ( filename.GetFullName() != text )
1877 {
1878 wxFileName fn = filename;
1879 fn.SetFullName(text);
1880 variant = fn.GetFullPath();
1881 return true;
1882 }
1883 }
1884
1885 return false;
1886 }
1887
1888 bool wxFileProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1889 {
1890 // Return false on some occasions to make sure those attribs will get
1891 // stored in m_attributes.
1892 if ( name == wxPG_FILE_SHOW_FULL_PATH )
1893 {
1894 if ( value.GetLong() )
1895 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1896 else
1897 m_flags &= ~(wxPG_PROP_SHOW_FULL_FILENAME);
1898 return true;
1899 }
1900 else if ( name == wxPG_FILE_WILDCARD )
1901 {
1902 m_wildcard = value.GetString();
1903 }
1904 else if ( name == wxPG_FILE_SHOW_RELATIVE_PATH )
1905 {
1906 m_basePath = value.GetString();
1907
1908 // Make sure wxPG_FILE_SHOW_FULL_PATH is also set
1909 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1910 }
1911 else if ( name == wxPG_FILE_INITIAL_PATH )
1912 {
1913 m_initialPath = value.GetString();
1914 return true;
1915 }
1916 else if ( name == wxPG_FILE_DIALOG_TITLE )
1917 {
1918 m_dlgTitle = value.GetString();
1919 return true;
1920 }
1921 return false;
1922 }
1923
1924 // -----------------------------------------------------------------------
1925 // wxPGLongStringDialogAdapter
1926 // -----------------------------------------------------------------------
1927
1928 bool wxPGLongStringDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1929 {
1930 wxString val1 = property->GetValueAsString(0);
1931 wxString val_orig = val1;
1932
1933 wxString value;
1934 if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
1935 wxPropertyGrid::ExpandEscapeSequences(value, val1);
1936 else
1937 value = wxString(val1);
1938
1939 // Run editor dialog.
1940 if ( wxLongStringProperty::DisplayEditorDialog(property, propGrid, value) )
1941 {
1942 if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
1943 wxPropertyGrid::CreateEscapeSequences(val1,value);
1944 else
1945 val1 = value;
1946
1947 if ( val1 != val_orig )
1948 {
1949 SetValue( val1 );
1950 return true;
1951 }
1952 }
1953 return false;
1954 }
1955
1956 // -----------------------------------------------------------------------
1957 // wxLongStringProperty
1958 // -----------------------------------------------------------------------
1959
1960 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxLongStringProperty,wxPGProperty,
1961 wxString,const wxString&,TextCtrlAndButton)
1962
1963 wxLongStringProperty::wxLongStringProperty( const wxString& label, const wxString& name,
1964 const wxString& value ) : wxPGProperty(label,name)
1965 {
1966 SetValue(value);
1967 }
1968
1969 wxLongStringProperty::~wxLongStringProperty() {}
1970
1971 wxString wxLongStringProperty::ValueToString( wxVariant& value,
1972 int WXUNUSED(argFlags) ) const
1973 {
1974 return value;
1975 }
1976
1977 bool wxLongStringProperty::OnEvent( wxPropertyGrid* propGrid, wxWindow* WXUNUSED(primary),
1978 wxEvent& event )
1979 {
1980 if ( propGrid->IsMainButtonEvent(event) )
1981 {
1982 // Update the value
1983 wxVariant useValue = propGrid->GetUncommittedPropertyValue();
1984
1985 wxString val1 = useValue.GetString();
1986 wxString val_orig = val1;
1987
1988 wxString value;
1989 if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
1990 wxPropertyGrid::ExpandEscapeSequences(value,val1);
1991 else
1992 value = wxString(val1);
1993
1994 // Run editor dialog.
1995 if ( OnButtonClick(propGrid,value) )
1996 {
1997 if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
1998 wxPropertyGrid::CreateEscapeSequences(val1,value);
1999 else
2000 val1 = value;
2001
2002 if ( val1 != val_orig )
2003 {
2004 SetValueInEvent( val1 );
2005 return true;
2006 }
2007 }
2008 }
2009 return false;
2010 }
2011
2012 bool wxLongStringProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
2013 {
2014 return DisplayEditorDialog(this, propGrid, value);
2015 }
2016
2017 bool wxLongStringProperty::DisplayEditorDialog( wxPGProperty* prop, wxPropertyGrid* propGrid, wxString& value )
2018
2019 {
2020 // launch editor dialog
2021 wxDialog* dlg = new wxDialog(propGrid,-1,prop->GetLabel(),wxDefaultPosition,wxDefaultSize,
2022 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxCLIP_CHILDREN);
2023
2024 dlg->SetFont(propGrid->GetFont()); // To allow entering chars of the same set as the propGrid
2025
2026 // Multi-line text editor dialog.
2027 #if !wxPG_SMALL_SCREEN
2028 const int spacing = 8;
2029 #else
2030 const int spacing = 4;
2031 #endif
2032 wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
2033 wxBoxSizer* rowsizer = new wxBoxSizer( wxHORIZONTAL );
2034 wxTextCtrl* ed = new wxTextCtrl(dlg,11,value,
2035 wxDefaultPosition,wxDefaultSize,wxTE_MULTILINE);
2036
2037 rowsizer->Add( ed, 1, wxEXPAND|wxALL, spacing );
2038 topsizer->Add( rowsizer, 1, wxEXPAND, 0 );
2039
2040 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
2041 buttonSizer->AddButton(new wxButton(dlg, wxID_OK));
2042 buttonSizer->AddButton(new wxButton(dlg, wxID_CANCEL));
2043 buttonSizer->Realize();
2044 topsizer->Add( buttonSizer, 0,
2045 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxBOTTOM|wxRIGHT,
2046 spacing );
2047
2048 dlg->SetSizer( topsizer );
2049 topsizer->SetSizeHints( dlg );
2050
2051 #if !wxPG_SMALL_SCREEN
2052 dlg->SetSize(400,300);
2053
2054 dlg->Move( propGrid->GetGoodEditorDialogPosition(prop,dlg->GetSize()) );
2055 #endif
2056
2057 int res = dlg->ShowModal();
2058
2059 if ( res == wxID_OK )
2060 {
2061 value = ed->GetValue();
2062 dlg->Destroy();
2063 return true;
2064 }
2065 dlg->Destroy();
2066 return false;
2067 }
2068
2069 bool wxLongStringProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
2070 {
2071 if ( variant != text )
2072 {
2073 variant = text;
2074 return true;
2075 }
2076 return false;
2077 }
2078
2079 #if wxUSE_EDITABLELISTBOX
2080
2081 // -----------------------------------------------------------------------
2082 // wxPGArrayEditorDialog
2083 // -----------------------------------------------------------------------
2084
2085 BEGIN_EVENT_TABLE(wxPGArrayEditorDialog, wxDialog)
2086 EVT_IDLE(wxPGArrayEditorDialog::OnIdle)
2087 END_EVENT_TABLE()
2088
2089 IMPLEMENT_ABSTRACT_CLASS(wxPGArrayEditorDialog, wxDialog)
2090
2091 #include "wx/editlbox.h"
2092 #include "wx/listctrl.h"
2093
2094 // -----------------------------------------------------------------------
2095
2096 void wxPGArrayEditorDialog::OnIdle(wxIdleEvent& event)
2097 {
2098 // Repair focus - wxEditableListBox has bitmap buttons, which
2099 // get focus, and lose focus (into the oblivion) when they
2100 // become disabled due to change in control state.
2101
2102 wxWindow* lastFocused = m_lastFocused;
2103 wxWindow* focus = ::wxWindow::FindFocus();
2104
2105 // If last focused control became disabled, set focus back to
2106 // wxEditableListBox
2107 if ( lastFocused && focus != lastFocused &&
2108 lastFocused->GetParent() == m_elbSubPanel &&
2109 !lastFocused->IsEnabled() )
2110 {
2111 m_elb->GetListCtrl()->SetFocus();
2112 }
2113
2114 m_lastFocused = focus;
2115
2116 event.Skip();
2117 }
2118
2119 // -----------------------------------------------------------------------
2120
2121 wxPGArrayEditorDialog::wxPGArrayEditorDialog()
2122 : wxDialog()
2123 {
2124 Init();
2125 }
2126
2127 // -----------------------------------------------------------------------
2128
2129 void wxPGArrayEditorDialog::Init()
2130 {
2131 m_lastFocused = NULL;
2132 m_hasCustomNewAction = false;
2133 m_itemPendingAtIndex = -1;
2134 }
2135
2136 // -----------------------------------------------------------------------
2137
2138 wxPGArrayEditorDialog::wxPGArrayEditorDialog( wxWindow *parent,
2139 const wxString& message,
2140 const wxString& caption,
2141 long style,
2142 const wxPoint& pos,
2143 const wxSize& sz )
2144 : wxDialog()
2145 {
2146 Init();
2147 Create(parent,message,caption,style,pos,sz);
2148 }
2149
2150 // -----------------------------------------------------------------------
2151
2152 bool wxPGArrayEditorDialog::Create( wxWindow *parent,
2153 const wxString& message,
2154 const wxString& caption,
2155 long style,
2156 const wxPoint& pos,
2157 const wxSize& sz )
2158 {
2159 // On wxMAC the dialog shows incorrectly if style is not exactly wxCAPTION
2160 // FIXME: This should be only a temporary fix.
2161 #ifdef __WXMAC__
2162 wxUnusedVar(style);
2163 int useStyle = wxCAPTION;
2164 #else
2165 int useStyle = style;
2166 #endif
2167
2168 bool res = wxDialog::Create(parent, wxID_ANY, caption, pos, sz, useStyle);
2169
2170 SetFont(parent->GetFont()); // To allow entering chars of the same set as the propGrid
2171
2172 #if !wxPG_SMALL_SCREEN
2173 const int spacing = 4;
2174 #else
2175 const int spacing = 3;
2176 #endif
2177
2178 m_modified = false;
2179
2180 wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
2181
2182 // Message
2183 if ( message.length() )
2184 topsizer->Add( new wxStaticText(this,-1,message),
2185 0, wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL|wxALL, spacing );
2186
2187 m_elb = new wxEditableListBox(this, wxID_ANY, message,
2188 wxDefaultPosition,
2189 wxDefaultSize,
2190 wxEL_ALLOW_NEW |
2191 wxEL_ALLOW_EDIT |
2192 wxEL_ALLOW_DELETE);
2193
2194 // Populate the list box
2195 wxArrayString arr;
2196 for ( unsigned int i=0; i<ArrayGetCount(); i++ )
2197 arr.push_back(ArrayGet(i));
2198 m_elb->SetStrings(arr);
2199
2200 // Connect event handlers
2201 wxButton* but;
2202 wxListCtrl* lc = m_elb->GetListCtrl();
2203
2204 but = m_elb->GetNewButton();
2205 m_elbSubPanel = but->GetParent();
2206 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2207 wxCommandEventHandler(wxPGArrayEditorDialog::OnAddClick),
2208 NULL, this);
2209
2210 but = m_elb->GetDelButton();
2211 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2212 wxCommandEventHandler(wxPGArrayEditorDialog::OnDeleteClick),
2213 NULL, this);
2214
2215 but = m_elb->GetUpButton();
2216 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2217 wxCommandEventHandler(wxPGArrayEditorDialog::OnUpClick),
2218 NULL, this);
2219
2220 but = m_elb->GetDownButton();
2221 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2222 wxCommandEventHandler(wxPGArrayEditorDialog::OnDownClick),
2223 NULL, this);
2224
2225 lc->Connect(lc->GetId(), wxEVT_COMMAND_LIST_END_LABEL_EDIT,
2226 wxListEventHandler(wxPGArrayEditorDialog::OnEndLabelEdit),
2227 NULL, this);
2228
2229 topsizer->Add( m_elb, 1, wxEXPAND, spacing );
2230
2231 // Standard dialog buttons
2232 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
2233 buttonSizer->AddButton(new wxButton(this, wxID_OK));
2234 buttonSizer->AddButton(new wxButton(this, wxID_CANCEL));
2235 buttonSizer->Realize();
2236 topsizer->Add( buttonSizer, 0,
2237 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxALL,
2238 spacing );
2239
2240 m_elb->SetFocus();
2241
2242 SetSizer( topsizer );
2243 topsizer->SetSizeHints( this );
2244
2245 #if !wxPG_SMALL_SCREEN
2246 if ( sz.x == wxDefaultSize.x &&
2247 sz.y == wxDefaultSize.y )
2248 SetSize( wxSize(275,360) );
2249 else
2250 SetSize(sz);
2251 #endif
2252
2253 return res;
2254 }
2255
2256 // -----------------------------------------------------------------------
2257
2258 int wxPGArrayEditorDialog::GetSelection() const
2259 {
2260 wxListCtrl* lc = m_elb->GetListCtrl();
2261
2262 int index = lc->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
2263 if ( index == -1 )
2264 return wxNOT_FOUND;
2265
2266 return index;
2267 }
2268
2269 // -----------------------------------------------------------------------
2270
2271 void wxPGArrayEditorDialog::OnAddClick(wxCommandEvent& event)
2272 {
2273 wxListCtrl* lc = m_elb->GetListCtrl();
2274 int newItemIndex = lc->GetItemCount() - 1;
2275
2276 if ( m_hasCustomNewAction )
2277 {
2278 wxString str;
2279 if ( OnCustomNewAction(&str) )
2280 {
2281 if ( ArrayInsert(str, newItemIndex) )
2282 {
2283 lc->InsertItem(newItemIndex, str);
2284 m_modified = true;
2285 }
2286 }
2287
2288 // Do *not* skip the event! We do not want the wxEditableListBox
2289 // to do anything.
2290 }
2291 else
2292 {
2293 m_itemPendingAtIndex = newItemIndex;
2294
2295 event.Skip();
2296 }
2297 }
2298
2299 // -----------------------------------------------------------------------
2300
2301 void wxPGArrayEditorDialog::OnDeleteClick(wxCommandEvent& event)
2302 {
2303 int index = GetSelection();
2304 if ( index >= 0 )
2305 {
2306 ArrayRemoveAt( index );
2307 m_modified = true;
2308 }
2309
2310 event.Skip();
2311 }
2312
2313 // -----------------------------------------------------------------------
2314
2315 void wxPGArrayEditorDialog::OnUpClick(wxCommandEvent& event)
2316 {
2317 int index = GetSelection();
2318 if ( index > 0 )
2319 {
2320 ArraySwap(index-1,index);
2321 m_modified = true;
2322 }
2323
2324 event.Skip();
2325 }
2326
2327 // -----------------------------------------------------------------------
2328
2329 void wxPGArrayEditorDialog::OnDownClick(wxCommandEvent& event)
2330 {
2331 wxListCtrl* lc = m_elb->GetListCtrl();
2332 int index = GetSelection();
2333 int lastStringIndex = lc->GetItemCount() - 1;
2334 if ( index >= 0 && index < lastStringIndex )
2335 {
2336 ArraySwap(index, index+1);
2337 m_modified = true;
2338 }
2339
2340 event.Skip();
2341 }
2342
2343 // -----------------------------------------------------------------------
2344
2345 void wxPGArrayEditorDialog::OnEndLabelEdit(wxListEvent& event)
2346 {
2347 wxString str = event.GetLabel();
2348
2349 if ( m_itemPendingAtIndex >= 0 )
2350 {
2351 // Add a new item
2352 if ( ArrayInsert(str, m_itemPendingAtIndex) )
2353 {
2354 m_modified = true;
2355 }
2356 else
2357 {
2358 // Editable list box doesn't really respect Veto(), but
2359 // it recognizes if no text was added, so we simulate
2360 // Veto() using it.
2361 event.m_item.SetText(wxEmptyString);
2362 m_elb->GetListCtrl()->SetItemText(m_itemPendingAtIndex,
2363 wxEmptyString);
2364
2365 event.Veto();
2366 }
2367 }
2368 else
2369 {
2370 // Change an existing item
2371 int index = GetSelection();
2372 wxASSERT( index != wxNOT_FOUND );
2373 if ( ArraySet(index, str) )
2374 m_modified = true;
2375 else
2376 event.Veto();
2377 }
2378
2379 event.Skip();
2380 }
2381
2382 #endif // wxUSE_EDITABLELISTBOX
2383
2384 // -----------------------------------------------------------------------
2385 // wxPGArrayStringEditorDialog
2386 // -----------------------------------------------------------------------
2387
2388 IMPLEMENT_DYNAMIC_CLASS(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2389
2390 BEGIN_EVENT_TABLE(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2391 END_EVENT_TABLE()
2392
2393 // -----------------------------------------------------------------------
2394
2395 wxString wxPGArrayStringEditorDialog::ArrayGet( size_t index )
2396 {
2397 return m_array[index];
2398 }
2399
2400 size_t wxPGArrayStringEditorDialog::ArrayGetCount()
2401 {
2402 return m_array.size();
2403 }
2404
2405 bool wxPGArrayStringEditorDialog::ArrayInsert( const wxString& str, int index )
2406 {
2407 if (index<0)
2408 m_array.Add(str);
2409 else
2410 m_array.Insert(str,index);
2411 return true;
2412 }
2413
2414 bool wxPGArrayStringEditorDialog::ArraySet( size_t index, const wxString& str )
2415 {
2416 m_array[index] = str;
2417 return true;
2418 }
2419
2420 void wxPGArrayStringEditorDialog::ArrayRemoveAt( int index )
2421 {
2422 m_array.RemoveAt(index);
2423 }
2424
2425 void wxPGArrayStringEditorDialog::ArraySwap( size_t first, size_t second )
2426 {
2427 wxString old_str = m_array[first];
2428 wxString new_str = m_array[second];
2429 m_array[first] = new_str;
2430 m_array[second] = old_str;
2431 }
2432
2433 wxPGArrayStringEditorDialog::wxPGArrayStringEditorDialog()
2434 : wxPGArrayEditorDialog()
2435 {
2436 Init();
2437 }
2438
2439 void wxPGArrayStringEditorDialog::Init()
2440 {
2441 m_pCallingClass = NULL;
2442 }
2443
2444 bool
2445 wxPGArrayStringEditorDialog::OnCustomNewAction(wxString* resString)
2446 {
2447 return m_pCallingClass->OnCustomStringEdit(m_parent, *resString);
2448 }
2449
2450 // -----------------------------------------------------------------------
2451 // wxArrayStringProperty
2452 // -----------------------------------------------------------------------
2453
2454 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayStringProperty, // Property name
2455 wxPGProperty, // Property we inherit from
2456 wxArrayString, // Value type name
2457 const wxArrayString&, // Value type, as given in constructor
2458 TextCtrlAndButton) // Initial editor
2459
2460 wxArrayStringProperty::wxArrayStringProperty( const wxString& label,
2461 const wxString& name,
2462 const wxArrayString& array )
2463 : wxPGProperty(label,name)
2464 {
2465 m_delimiter = ',';
2466 SetValue( array );
2467 }
2468
2469 wxArrayStringProperty::~wxArrayStringProperty() { }
2470
2471 void wxArrayStringProperty::OnSetValue()
2472 {
2473 GenerateValueAsString();
2474 }
2475
2476 void
2477 wxArrayStringProperty::ConvertArrayToString(const wxArrayString& arr,
2478 wxString* pString,
2479 const wxUniChar& delimiter) const
2480 {
2481 if ( delimiter == '"' || delimiter == '\'' )
2482 {
2483 // Quoted strings
2484 ArrayStringToString(*pString,
2485 arr,
2486 delimiter,
2487 Escape | QuoteStrings);
2488 }
2489 else
2490 {
2491 // Regular delimiter
2492 ArrayStringToString(*pString,
2493 arr,
2494 delimiter,
2495 0);
2496 }
2497 }
2498
2499 wxString wxArrayStringProperty::ValueToString( wxVariant& WXUNUSED(value),
2500 int argFlags ) const
2501 {
2502 //
2503 // If this is called from GetValueAsString(), return cached string
2504 if ( argFlags & wxPG_VALUE_IS_CURRENT )
2505 {
2506 return m_display;
2507 }
2508
2509 wxArrayString arr = m_value.GetArrayString();
2510 wxString s;
2511 ConvertArrayToString(arr, &s, m_delimiter);
2512 return s;
2513 }
2514
2515 // Converts wxArrayString to a string separated by delimeters and spaces.
2516 // preDelim is useful for "str1" "str2" style. Set flags to 1 to do slash
2517 // conversion.
2518 void
2519 wxArrayStringProperty::ArrayStringToString( wxString& dst,
2520 const wxArrayString& src,
2521 wxUniChar delimiter, int flags )
2522 {
2523 wxString pdr;
2524 wxString preas;
2525
2526 unsigned int i;
2527 unsigned int itemCount = src.size();
2528
2529 dst.Empty();
2530
2531 if ( flags & Escape )
2532 {
2533 preas = delimiter;
2534 pdr = wxS("\\") + static_cast<wchar_t>(delimiter);
2535 }
2536
2537 if ( itemCount )
2538 dst.append( preas );
2539
2540 wxString delimStr(delimiter);
2541
2542 for ( i = 0; i < itemCount; i++ )
2543 {
2544 wxString str( src.Item(i) );
2545
2546 // Do some character conversion.
2547 // Converts \ to \\ and $delimiter to \$delimiter
2548 // Useful when quoting.
2549 if ( flags & Escape )
2550 {
2551 str.Replace( wxS("\\"), wxS("\\\\"), true );
2552 if ( pdr.length() )
2553 str.Replace( preas, pdr, true );
2554 }
2555
2556 dst.append( str );
2557
2558 if ( i < (itemCount-1) )
2559 {
2560 dst.append( delimStr );
2561 dst.append( wxS(" ") );
2562 dst.append( preas );
2563 }
2564 else if ( flags & QuoteStrings )
2565 dst.append( delimStr );
2566 }
2567 }
2568
2569 void wxArrayStringProperty::GenerateValueAsString()
2570 {
2571 wxArrayString arr = m_value.GetArrayString();
2572 ConvertArrayToString(arr, &m_display, m_delimiter);
2573 }
2574
2575 // Default implementation doesn't do anything.
2576 bool wxArrayStringProperty::OnCustomStringEdit( wxWindow*, wxString& )
2577 {
2578 return false;
2579 }
2580
2581 wxPGArrayEditorDialog* wxArrayStringProperty::CreateEditorDialog()
2582 {
2583 return new wxPGArrayStringEditorDialog();
2584 }
2585
2586 bool wxArrayStringProperty::OnButtonClick( wxPropertyGrid* propGrid,
2587 wxWindow* WXUNUSED(primaryCtrl),
2588 const wxChar* cbt )
2589 {
2590 // Update the value
2591 wxVariant useValue = propGrid->GetUncommittedPropertyValue();
2592
2593 if ( !propGrid->EditorValidate() )
2594 return false;
2595
2596 // Create editor dialog.
2597 wxPGArrayEditorDialog* dlg = CreateEditorDialog();
2598 #if wxUSE_VALIDATORS
2599 wxValidator* validator = GetValidator();
2600 wxPGInDialogValidator dialogValidator;
2601 #endif
2602
2603 wxPGArrayStringEditorDialog* strEdDlg = wxDynamicCast(dlg, wxPGArrayStringEditorDialog);
2604
2605 if ( strEdDlg )
2606 strEdDlg->SetCustomButton(cbt, this);
2607
2608 dlg->SetDialogValue( useValue );
2609 dlg->Create(propGrid, wxEmptyString, m_label);
2610
2611 #if !wxPG_SMALL_SCREEN
2612 dlg->Move( propGrid->GetGoodEditorDialogPosition(this,dlg->GetSize()) );
2613 #endif
2614
2615 bool retVal;
2616
2617 for (;;)
2618 {
2619 retVal = false;
2620
2621 int res = dlg->ShowModal();
2622
2623 if ( res == wxID_OK && dlg->IsModified() )
2624 {
2625 wxVariant value = dlg->GetDialogValue();
2626 if ( !value.IsNull() )
2627 {
2628 wxArrayString actualValue = value.GetArrayString();
2629 wxString tempStr;
2630 ConvertArrayToString(actualValue, &tempStr, m_delimiter);
2631 #if wxUSE_VALIDATORS
2632 if ( dialogValidator.DoValidate(propGrid, validator,
2633 tempStr) )
2634 #endif
2635 {
2636 SetValueInEvent( actualValue );
2637 retVal = true;
2638 break;
2639 }
2640 }
2641 else
2642 break;
2643 }
2644 else
2645 break;
2646 }
2647
2648 delete dlg;
2649
2650 return retVal;
2651 }
2652
2653 bool wxArrayStringProperty::OnEvent( wxPropertyGrid* propGrid,
2654 wxWindow* primary,
2655 wxEvent& event )
2656 {
2657 if ( propGrid->IsMainButtonEvent(event) )
2658 return OnButtonClick(propGrid,primary,(const wxChar*) NULL);
2659 return false;
2660 }
2661
2662 bool wxArrayStringProperty::StringToValue( wxVariant& variant,
2663 const wxString& text, int ) const
2664 {
2665 wxArrayString arr;
2666
2667 if ( m_delimiter == '"' || m_delimiter == '\'' )
2668 {
2669 // Quoted strings
2670 WX_PG_TOKENIZER2_BEGIN(text, m_delimiter)
2671
2672 // Need to replace backslashes with empty characters
2673 // (opposite what is done in ConvertArrayToString()).
2674 token.Replace ( wxS("\\\\"), wxS("\\"), true );
2675
2676 arr.Add( token );
2677
2678 WX_PG_TOKENIZER2_END()
2679 }
2680 else
2681 {
2682 // Regular delimiter
2683 WX_PG_TOKENIZER1_BEGIN(text, m_delimiter)
2684 arr.Add( token );
2685 WX_PG_TOKENIZER1_END()
2686 }
2687
2688 variant = arr;
2689
2690 return true;
2691 }
2692
2693 bool wxArrayStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
2694 {
2695 if ( name == wxPG_ARRAY_DELIMITER )
2696 {
2697 m_delimiter = value.GetChar();
2698 GenerateValueAsString();
2699 return false;
2700 }
2701 return true;
2702 }
2703
2704 // -----------------------------------------------------------------------
2705 // wxPGInDialogValidator
2706 // -----------------------------------------------------------------------
2707
2708 #if wxUSE_VALIDATORS
2709 bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* propGrid,
2710 wxValidator* validator,
2711 const wxString& value )
2712 {
2713 if ( !validator )
2714 return true;
2715
2716 wxTextCtrl* tc = m_textCtrl;
2717
2718 if ( !tc )
2719 {
2720 {
2721 tc = new wxTextCtrl( propGrid, wxPG_SUBID_TEMP1, wxEmptyString,
2722 wxPoint(30000,30000));
2723 tc->Hide();
2724 }
2725
2726 m_textCtrl = tc;
2727 }
2728
2729 tc->SetValue(value);
2730
2731 validator->SetWindow(tc);
2732 bool res = validator->Validate(propGrid);
2733
2734 return res;
2735 }
2736 #else
2737 bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* WXUNUSED(propGrid),
2738 wxValidator* WXUNUSED(validator),
2739 const wxString& WXUNUSED(value) )
2740 {
2741 return true;
2742 }
2743 #endif
2744
2745 // -----------------------------------------------------------------------
2746
2747 #endif // wxUSE_PROPGRID